De-recursify source tree
authorColin Walters <walters@verbum.org>
Wed, 2 Nov 2011 19:45:32 +0000 (15:45 -0400)
committerColin Walters <walters@verbum.org>
Wed, 2 Nov 2011 19:45:32 +0000 (15:45 -0400)
59 files changed:
Makefile-libostree.am [new file with mode: 0644]
Makefile-ostree.am [new file with mode: 0644]
Makefile-otutil.am [new file with mode: 0644]
Makefile-src.am [deleted file]
Makefile.am
libostree/ostree-checkout.c [new file with mode: 0644]
libostree/ostree-checkout.h [new file with mode: 0644]
libostree/ostree-core.c [new file with mode: 0644]
libostree/ostree-core.h [new file with mode: 0644]
libostree/ostree-repo.c [new file with mode: 0644]
libostree/ostree-repo.h [new file with mode: 0644]
libostree/ostree.h [new file with mode: 0644]
libotutil/ot-gio-utils.c [new file with mode: 0644]
libotutil/ot-gio-utils.h [new file with mode: 0644]
libotutil/ot-opt-utils.c [new file with mode: 0644]
libotutil/ot-opt-utils.h [new file with mode: 0644]
libotutil/ot-unix-utils.c [new file with mode: 0644]
libotutil/ot-unix-utils.h [new file with mode: 0644]
libotutil/otutil.h [new file with mode: 0644]
ostree/main.c [new file with mode: 0644]
ostree/ot-builtin-checkout.c [new file with mode: 0644]
ostree/ot-builtin-commit.c [new file with mode: 0644]
ostree/ot-builtin-fsck.c [new file with mode: 0644]
ostree/ot-builtin-init.c [new file with mode: 0644]
ostree/ot-builtin-link-file.c [new file with mode: 0644]
ostree/ot-builtin-log.c [new file with mode: 0644]
ostree/ot-builtin-pull.c [new file with mode: 0644]
ostree/ot-builtin-remote.c [new file with mode: 0644]
ostree/ot-builtin-rev-parse.c [new file with mode: 0644]
ostree/ot-builtin-run-triggers.c [new file with mode: 0644]
ostree/ot-builtin-show.c [new file with mode: 0644]
ostree/ot-builtins.h [new file with mode: 0644]
src/libostree/ostree-checkout.c [deleted file]
src/libostree/ostree-checkout.h [deleted file]
src/libostree/ostree-core.c [deleted file]
src/libostree/ostree-core.h [deleted file]
src/libostree/ostree-repo.c [deleted file]
src/libostree/ostree-repo.h [deleted file]
src/libostree/ostree.h [deleted file]
src/libotutil/ot-gio-utils.c [deleted file]
src/libotutil/ot-gio-utils.h [deleted file]
src/libotutil/ot-opt-utils.c [deleted file]
src/libotutil/ot-opt-utils.h [deleted file]
src/libotutil/ot-unix-utils.c [deleted file]
src/libotutil/ot-unix-utils.h [deleted file]
src/libotutil/otutil.h [deleted file]
src/main.c [deleted file]
src/ot-builtin-checkout.c [deleted file]
src/ot-builtin-commit.c [deleted file]
src/ot-builtin-fsck.c [deleted file]
src/ot-builtin-init.c [deleted file]
src/ot-builtin-link-file.c [deleted file]
src/ot-builtin-log.c [deleted file]
src/ot-builtin-pull.c [deleted file]
src/ot-builtin-remote.c [deleted file]
src/ot-builtin-rev-parse.c [deleted file]
src/ot-builtin-run-triggers.c [deleted file]
src/ot-builtin-show.c [deleted file]
src/ot-builtins.h [deleted file]

diff --git a/Makefile-libostree.am b/Makefile-libostree.am
new file mode 100644 (file)
index 0000000..7b8790b
--- /dev/null
@@ -0,0 +1,32 @@
+# Makefile for C source code
+#
+# Copyright (C) 2011 Colin Walters <walters@verbum.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Author: Colin Walters <walters@verbum.org>
+
+noinst_LTLIBRARIES += libostree.la
+
+libostree_la_SOURCES = libostree/ostree.h \
+       libostree/ostree-core.c \
+       libostree/ostree-core.h \
+       libostree/ostree-repo.c \
+       libostree/ostree-repo.h \
+       libostree/ostree-checkout.c \
+       libostree/ostree-checkout.h \
+       $(NULL)
+libostree_la_CFLAGS = -I$(srcdir)/libotutil -I$(srcdir)/libostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+libostree_la_LIBADD = libotutil.la $(OT_COREBIN_DEP_LIBS)
diff --git a/Makefile-ostree.am b/Makefile-ostree.am
new file mode 100644 (file)
index 0000000..01ffbec
--- /dev/null
@@ -0,0 +1,38 @@
+# Makefile for C source code
+#
+# Copyright (C) 2011 Colin Walters <walters@verbum.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Author: Colin Walters <walters@verbum.org>
+
+bin_PROGRAMS += ostree
+
+ostree_SOURCES = ostree/main.c \
+       ostree/ot-builtins.h \
+       ostree/ot-builtin-checkout.c \
+       ostree/ot-builtin-commit.c \
+       ostree/ot-builtin-fsck.c \
+       ostree/ot-builtin-init.c \
+       ostree/ot-builtin-link-file.c \
+       ostree/ot-builtin-log.c \
+       ostree/ot-builtin-pull.c \
+       ostree/ot-builtin-run-triggers.c \
+       ostree/ot-builtin-remote.c \
+       ostree/ot-builtin-rev-parse.c \
+       ostree/ot-builtin-show.c \
+       $(NULL)
+ostree_CFLAGS = -I$(srcdir)/src -I$(srcdir)/libostree -I$(srcdir)/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+ostree_LDADD = libotutil.la libostree.la $(OT_COREBIN_DEP_LIBS)
diff --git a/Makefile-otutil.am b/Makefile-otutil.am
new file mode 100644 (file)
index 0000000..8df7c5b
--- /dev/null
@@ -0,0 +1,33 @@
+# Makefile for C source code
+#
+# Copyright (C) 2011 Colin Walters <walters@verbum.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Author: Colin Walters <walters@verbum.org>
+
+noinst_LTLIBRARIES += libotutil.la
+
+libotutil_la_SOURCES = \
+       libotutil/ot-opt-utils.c \
+       libotutil/ot-opt-utils.h \
+       libotutil/ot-unix-utils.c \
+       libotutil/ot-unix-utils.h \
+       libotutil/ot-gio-utils.c \
+       libotutil/ot-gio-utils.h \
+       libotutil/otutil.h \
+       $(NULL)
+libotutil_la_CFLAGS = -I$(srcdir)/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
+libotutil_la_LIBADD = $(GIO_UNIX_LIBS)
diff --git a/Makefile-src.am b/Makefile-src.am
deleted file mode 100644 (file)
index 33657e6..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-# Makefile for C source code
-#
-# Copyright (C) 2011 Colin Walters <walters@verbum.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# Author: Colin Walters <walters@verbum.org>
-
-noinst_LTLIBRARIES += libotutil.la
-
-libotutil_la_SOURCES = \
-       src/libotutil/ot-opt-utils.c \
-       src/libotutil/ot-opt-utils.h \
-       src/libotutil/ot-unix-utils.c \
-       src/libotutil/ot-unix-utils.h \
-       src/libotutil/ot-gio-utils.c \
-       src/libotutil/ot-gio-utils.h \
-       src/libotutil/otutil.h \
-       $(NULL)
-libotutil_la_CFLAGS = -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
-libotutil_la_LIBADD = $(GIO_UNIX_LIBS)
-
-noinst_LTLIBRARIES += libostree.la
-
-libostree_la_SOURCES = src/libostree/ostree.h \
-       src/libostree/ostree-core.c \
-       src/libostree/ostree-core.h \
-       src/libostree/ostree-repo.c \
-       src/libostree/ostree-repo.h \
-       src/libostree/ostree-checkout.c \
-       src/libostree/ostree-checkout.h \
-       $(NULL)
-libostree_la_CFLAGS = -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
-libostree_la_LIBADD = libotutil.la $(OT_COREBIN_DEP_LIBS)
-
-bin_PROGRAMS += ostree
-
-ostree_SOURCES = src/main.c \
-       src/ot-builtins.h \
-       src/ot-builtin-checkout.c \
-       src/ot-builtin-commit.c \
-       src/ot-builtin-fsck.c \
-       src/ot-builtin-init.c \
-       src/ot-builtin-link-file.c \
-       src/ot-builtin-log.c \
-       src/ot-builtin-pull.c \
-       src/ot-builtin-run-triggers.c \
-       src/ot-builtin-remote.c \
-       src/ot-builtin-rev-parse.c \
-       src/ot-builtin-show.c \
-       $(NULL)
-ostree_CFLAGS = -I$(srcdir)/src -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
-ostree_LDADD = libotutil.la libostree.la $(OT_COREBIN_DEP_LIBS)
index 1b28adf07b1ee5ee25b191bf12ee619517c0d5f3..3dbda7212fa4adec431d5177767915bfe0d1938c 100644 (file)
@@ -11,5 +11,7 @@ libexec_PROGRAMS =
 noinst_LTLIBRARIES =
 noinst_PROGRAMS =
 
-include Makefile-src.am
+include Makefile-otutil.am
+include Makefile-libostree.am
+include Makefile-ostree.am
 include Makefile-triggers.am
diff --git a/libostree/ostree-checkout.c b/libostree/ostree-checkout.c
new file mode 100644 (file)
index 0000000..0569f28
--- /dev/null
@@ -0,0 +1,361 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+
+enum {
+  PROP_0,
+
+  PROP_REPO,
+  PROP_PATH
+};
+
+G_DEFINE_TYPE (OstreeCheckout, ostree_checkout, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_CHECKOUT, OstreeCheckoutPrivate))
+
+typedef struct _OstreeCheckoutPrivate OstreeCheckoutPrivate;
+
+struct _OstreeCheckoutPrivate {
+  OstreeRepo *repo;
+  char *path;
+};
+
+static void
+ostree_checkout_finalize (GObject *object)
+{
+  OstreeCheckout *self = OSTREE_CHECKOUT (object);
+  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+  g_free (priv->path);
+  g_clear_object (&priv->repo);
+
+  G_OBJECT_CLASS (ostree_checkout_parent_class)->finalize (object);
+}
+
+static void
+ostree_checkout_set_property(GObject         *object,
+                          guint            prop_id,
+                          const GValue    *value,
+                          GParamSpec      *pspec)
+{
+  OstreeCheckout *self = OSTREE_CHECKOUT (object);
+  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+  switch (prop_id)
+    {
+    case PROP_PATH:
+      priv->path = g_value_dup_string (value);
+      break;
+    case PROP_REPO:
+      priv->repo = g_value_dup_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+ostree_checkout_get_property(GObject         *object,
+                          guint            prop_id,
+                          GValue          *value,
+                          GParamSpec      *pspec)
+{
+  OstreeCheckout *self = OSTREE_CHECKOUT (object);
+  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+  switch (prop_id)
+    {
+    case PROP_PATH:
+      g_value_set_string (value, priv->path);
+      break;
+    case PROP_REPO:
+      g_value_set_object (value, priv->repo);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static GObject *
+ostree_checkout_constructor (GType                  gtype,
+                           guint                  n_properties,
+                           GObjectConstructParam *properties)
+{
+  GObject *object;
+  GObjectClass *parent_class;
+  OstreeCheckoutPrivate *priv;
+
+  parent_class = G_OBJECT_CLASS (ostree_checkout_parent_class);
+  object = parent_class->constructor (gtype, n_properties, properties);
+
+  priv = GET_PRIVATE (object);
+
+  g_assert (priv->path != NULL);
+  
+  return object;
+}
+
+static void
+ostree_checkout_class_init (OstreeCheckoutClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (OstreeCheckoutPrivate));
+
+  object_class->constructor = ostree_checkout_constructor;
+  object_class->get_property = ostree_checkout_get_property;
+  object_class->set_property = ostree_checkout_set_property;
+  object_class->finalize = ostree_checkout_finalize;
+
+  g_object_class_install_property (object_class,
+                                   PROP_PATH,
+                                   g_param_spec_string ("path", "", "",
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  g_object_class_install_property (object_class,
+                                   PROP_REPO,
+                                   g_param_spec_object ("repo", "", "",
+                                                        OSTREE_TYPE_REPO,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+ostree_checkout_init (OstreeCheckout *self)
+{
+}
+
+OstreeCheckout*
+ostree_checkout_new (OstreeRepo  *repo,
+                     const char  *path)
+{
+  return g_object_new (OSTREE_TYPE_CHECKOUT, "repo", repo, "path", path, NULL);
+}
+
+static gboolean
+executable_exists_in_checkout (const char *path,
+                               const char *executable)
+{
+  int i;
+  const char *subdirs[] = {"bin", "sbin", "usr/bin", "usr/sbin"};
+
+  for (i = 0; i < G_N_ELEMENTS (subdirs); i++)
+    {
+      char *possible_path = g_build_filename (path, subdirs[i], executable, NULL);
+      gboolean exists;
+      
+      exists = g_file_test (possible_path, G_FILE_TEST_EXISTS);
+      g_free (possible_path);
+
+      if (exists)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+run_trigger (OstreeCheckout *self,
+             GFile          *trigger,
+             gboolean        requires_chroot,
+             GError        **error)
+{
+  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  char *path = NULL;
+  char *temp_path = NULL;
+  char *rel_temp_path = NULL;
+  GFile *temp_copy = NULL;
+  char *basename = NULL;
+  GPtrArray *args = NULL;
+  int estatus;
+
+  path = g_file_get_path (trigger);
+  basename = g_path_get_basename (path);
+
+  args = g_ptr_array_new ();
+  
+  if (requires_chroot)
+    {
+      temp_path = g_build_filename (priv->path, basename, NULL);
+      rel_temp_path = g_strconcat ("./", basename, NULL);
+      temp_copy = ot_util_new_file_for_path (temp_path);
+
+      if (!g_file_copy (trigger, temp_copy, 0, NULL, NULL, NULL, error))
+        goto out;
+
+      g_ptr_array_add (args, "chroot");
+      g_ptr_array_add (args, ".");
+      g_ptr_array_add (args, rel_temp_path);
+      g_ptr_array_add (args, NULL);
+    }
+  else
+    {
+      g_ptr_array_add (args, path);
+      g_ptr_array_add (args, NULL);
+    }
+      
+  g_print ("Running trigger: %s\n", path);
+  if (!g_spawn_sync (priv->path,
+                     (char**)args->pdata,
+                     NULL,
+                     G_SPAWN_SEARCH_PATH,
+                     NULL, NULL, NULL, NULL,
+                     &estatus,
+                     error))
+    {
+      g_prefix_error (error, "Failed to run trigger %s: ", basename);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  if (requires_chroot && temp_path)
+    (void)unlink (temp_path);
+    
+  g_free (path);
+  g_free (basename);
+  g_free (temp_path);
+  g_free (rel_temp_path);
+  g_clear_object (&temp_copy);
+  if (args)
+    g_ptr_array_free (args, TRUE);
+  return ret;
+}
+
+static gboolean
+check_trigger (OstreeCheckout *self,
+               GFile          *trigger,
+               GError        **error)
+{
+  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  GInputStream *instream = NULL;
+  GDataInputStream *datain = NULL;
+  GError *temp_error = NULL;
+  char *line;
+  gsize len;
+  gboolean requires_chroot = TRUE;
+  gboolean matches = FALSE;
+
+  instream = (GInputStream*)g_file_read (trigger, NULL, error);
+  if (!instream)
+    goto out;
+  datain = g_data_input_stream_new (instream);
+
+  while ((line = g_data_input_stream_read_line (datain, &len, NULL, &temp_error)) != NULL)
+    {
+      if (g_str_has_prefix (line, "# IfExecutable: "))
+        {
+          char *executable = g_strdup (line + strlen ("# IfExecutable: "));
+          g_strchomp (executable);
+          matches = executable_exists_in_checkout (priv->path, executable);
+          g_free (executable);
+        }
+
+      g_free (line);
+    }
+  if (line == NULL && temp_error != NULL)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+  if (matches)
+    {
+      if (!run_trigger (self, trigger, requires_chroot, error))
+        goto out;
+    }
+  
+  ret = TRUE;
+ out:
+  g_clear_object (&instream);
+  g_clear_object (&datain);
+  return ret;
+}
+
+gboolean
+ostree_checkout_run_triggers (OstreeCheckout *self,
+                              GError        **error)
+{
+  gboolean ret = FALSE;
+  GError *temp_error = NULL;
+  char *triggerdir_path = NULL;
+  GFile *triggerdir = NULL;
+  GFileInfo *file_info = NULL;
+  GFileEnumerator *enumerator = NULL;
+
+  triggerdir_path = g_build_filename (LIBEXECDIR, "ostree", "triggers.d", NULL);
+  triggerdir = ot_util_new_file_for_path (triggerdir_path);
+
+  enumerator = g_file_enumerate_children (triggerdir, "standard::name,standard::type,unix::*", 
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          NULL, 
+                                          error);
+  if (!enumerator)
+    goto out;
+
+  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+    {
+      const char *name;
+      guint32 type;
+      char *child_path = NULL;
+      GFile *child = NULL;
+      gboolean success;
+
+      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
+      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+      
+      if (type == G_FILE_TYPE_REGULAR && g_str_has_suffix (name, ".trigger"))
+        {
+          child_path = g_build_filename (triggerdir_path, name, NULL);
+          child = ot_util_new_file_for_path (child_path);
+
+          success = check_trigger (self, child, error);
+        }
+      else
+        success = TRUE;
+
+      g_object_unref (file_info);
+      g_free (child_path);
+      g_clear_object (&child);
+      if (!success)
+        goto out;
+    }
+  if (file_info == NULL && temp_error != NULL)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  g_free (triggerdir_path);
+  g_clear_object (&triggerdir);
+  g_clear_object (&enumerator);
+  return ret;
+}
diff --git a/libostree/ostree-checkout.h b/libostree/ostree-checkout.h
new file mode 100644 (file)
index 0000000..375b977
--- /dev/null
@@ -0,0 +1,59 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_CHECKOUT
+#define _OSTREE_CHECKOUT
+
+#include <ostree-repo.h>
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_CHECKOUT ostree_checkout_get_type()
+#define OSTREE_CHECKOUT(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckout))
+#define OSTREE_CHECKOUT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
+#define OSTREE_IS_CHECKOUT(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_CHECKOUT))
+#define OSTREE_IS_CHECKOUT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_CHECKOUT))
+#define OSTREE_CHECKOUT_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
+
+typedef struct {
+  GObject parent;
+} OstreeCheckout;
+
+typedef struct {
+  GObjectClass parent_class;
+} OstreeCheckoutClass;
+
+GType ostree_checkout_get_type (void);
+
+OstreeCheckout* ostree_checkout_new (OstreeRepo  *repo,
+                                     const char  *path);
+
+gboolean ostree_checkout_run_triggers (OstreeCheckout *checkout,
+                                       GError        **error);
+
+G_END_DECLS
+
+#endif /* _OSTREE_CHECKOUT */
diff --git a/libostree/ostree-core.c b/libostree/ostree-core.c
new file mode 100644 (file)
index 0000000..d9027a3
--- /dev/null
@@ -0,0 +1,814 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+
+#include <sys/types.h>
+#include <attr/xattr.h>
+
+gboolean
+ostree_validate_checksum_string (const char *sha256,
+                                 GError    **error)
+{
+  if (strlen (sha256) != 64)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid rev '%s'", sha256);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+
+void
+ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode)
+{
+  guint32 perms = (mode & ~S_IFMT);
+  g_checksum_update (checksum, (guint8*) &uid, 4);
+  g_checksum_update (checksum, (guint8*) &gid, 4);
+  g_checksum_update (checksum, (guint8*) &perms, 4);
+}
+
+static char *
+canonicalize_xattrs (char *xattr_string, size_t len)
+{
+  char *p;
+  GSList *xattrs = NULL;
+  GSList *iter;
+  GString *result;
+
+  result = g_string_new (0);
+
+  p = xattr_string;
+  while (p < xattr_string+len)
+    {
+      xattrs = g_slist_prepend (xattrs, p);
+      p += strlen (p) + 1;
+    }
+
+  xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
+  for (iter = xattrs; iter; iter = iter->next)
+    g_string_append (result, iter->data);
+
+  g_slist_free (xattrs);
+  return g_string_free (result, FALSE);
+}
+
+static gboolean
+read_xattr_name_array (const char *path,
+                       const char *xattrs,
+                       size_t      len,
+                       GVariantBuilder *builder,
+                       GError  **error)
+{
+  gboolean ret = FALSE;
+  const char *p;
+
+  p = xattrs;
+  while (p < xattrs+len)
+    {
+      ssize_t bytes_read;
+      char *buf;
+
+      bytes_read = lgetxattr (path, p, NULL, 0);
+      if (bytes_read < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+      if (bytes_read == 0)
+        continue;
+
+      buf = g_malloc (bytes_read);
+      if (lgetxattr (path, p, buf, bytes_read) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          g_free (buf);
+          goto out;
+        }
+      
+      g_variant_builder_add (builder, "(@ay@ay)",
+                             g_variant_new_bytestring (p),
+                             g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
+                                                      buf, bytes_read, FALSE, g_free, buf));
+
+      p = p + strlen (p) + 1;
+    }
+  
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+GVariant *
+ostree_get_xattrs_for_path (const char *path,
+                              GError    **error)
+{
+  GVariant *ret = NULL;
+  GVariantBuilder builder;
+  char *xattr_names = NULL;
+  char *xattr_names_canonical = NULL;
+  ssize_t bytes_read;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
+
+  bytes_read = llistxattr (path, NULL, 0);
+
+  if (bytes_read < 0)
+    {
+      if (errno != ENOTSUP)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+  else if (bytes_read > 0)
+    {
+      xattr_names = g_malloc (bytes_read);
+      if (llistxattr (path, xattr_names, bytes_read) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+      xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
+      
+      if (!read_xattr_name_array (path, xattr_names_canonical, bytes_read, &builder, error))
+        goto out;
+    }
+
+  ret = g_variant_builder_end (&builder);
+  g_variant_ref_sink (ret);
+ out:
+  if (!ret)
+    g_variant_builder_clear (&builder);
+  g_free (xattr_names);
+  g_free (xattr_names_canonical);
+  return ret;
+}
+
+gboolean
+ostree_stat_and_checksum_file (int dir_fd, const char *path,
+                               OstreeObjectType objtype,
+                               GChecksum **out_checksum,
+                               struct stat *out_stbuf,
+                               GError **error)
+{
+  GChecksum *content_sha256 = NULL;
+  GChecksum *content_and_meta_sha256 = NULL;
+  char *stat_string = NULL;
+  ssize_t bytes_read;
+  GVariant *xattrs = NULL;
+  int fd = -1;
+  DIR *temp_dir = NULL;
+  char *basename = NULL;
+  gboolean ret = FALSE;
+  char *symlink_target = NULL;
+  char *device_id = NULL;
+  struct stat stbuf;
+
+  basename = g_path_get_basename (path);
+
+  if (dir_fd == -1)
+    {
+      char *dirname = g_path_get_dirname (path);
+      temp_dir = opendir (dirname);
+      if (temp_dir == NULL)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          g_free (dirname);
+        }
+      g_free (dirname);
+      dir_fd = dirfd (temp_dir);
+    }
+
+  if (fstatat (dir_fd, basename, &stbuf, AT_SYMLINK_NOFOLLOW) < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  if (S_ISREG(stbuf.st_mode))
+    {
+      fd = ot_util_open_file_read_at (dir_fd, basename, error);
+      if (fd < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+
+  if (objtype == OSTREE_OBJECT_TYPE_FILE)
+    {
+      xattrs = ostree_get_xattrs_for_path (path, error);
+      if (!xattrs)
+        goto out;
+    }
+
+  content_sha256 = g_checksum_new (G_CHECKSUM_SHA256);
+  if (S_ISREG(stbuf.st_mode))
+    {
+      guint8 buf[8192];
+
+      while ((bytes_read = read (fd, buf, sizeof (buf))) > 0)
+        g_checksum_update (content_sha256, buf, bytes_read);
+      if (bytes_read < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+  else if (S_ISLNK(stbuf.st_mode))
+    {
+      symlink_target = g_malloc (PATH_MAX);
+
+      g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
+      
+      bytes_read = readlinkat (dir_fd, basename, symlink_target, PATH_MAX);
+      if (bytes_read < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+      g_checksum_update (content_sha256, (guint8*)symlink_target, bytes_read);
+    }
+  else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode))
+    {
+      g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
+      device_id = g_strdup_printf ("%u", (guint)stbuf.st_rdev);
+      g_checksum_update (content_sha256, (guint8*)device_id, strlen (device_id));
+    }
+  else
+    {
+      g_set_error (error, G_IO_ERROR,
+                   G_IO_ERROR_FAILED,
+                   "Unsupported file '%s' (must be regular, symbolic link, or device)",
+                   path);
+      goto out;
+    }
+
+  content_and_meta_sha256 = g_checksum_copy (content_sha256);
+
+  if (objtype == OSTREE_OBJECT_TYPE_FILE)
+    {
+      ostree_checksum_update_stat (content_and_meta_sha256, stbuf.st_uid,
+                                   stbuf.st_gid, stbuf.st_mode);
+      g_checksum_update (content_and_meta_sha256, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+    }
+
+  *out_stbuf = stbuf;
+  *out_checksum = content_and_meta_sha256;
+  ret = TRUE;
+ out:
+  if (fd >= 0)
+    close (fd);
+  if (temp_dir != NULL)
+    closedir (temp_dir);
+  g_free (symlink_target);
+  g_free (basename);
+  g_free (stat_string);
+  if (xattrs)
+    g_variant_unref (xattrs);
+  if (content_sha256)
+    g_checksum_free (content_sha256);
+  return ret;
+}
+
+gboolean
+ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error)
+{
+  gboolean ret = FALSE;
+  int i, n;
+
+  n = g_variant_n_children (xattrs);
+  for (i = 0; i < n; i++)
+    {
+      const guint8* name;
+      GVariant *value;
+      const guint8* value_data;
+      gsize value_len;
+      gboolean loop_err;
+
+      g_variant_get_child (xattrs, i, "(^&ay@ay)",
+                           &name, &value);
+      value_data = g_variant_get_fixed_array (value, &value_len, 1);
+      
+      loop_err = lsetxattr (path, (char*)name, (char*)value_data, value_len, XATTR_REPLACE) < 0;
+      
+      g_variant_unref (value);
+      if (loop_err)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+gboolean
+ostree_parse_metadata_file (const char                  *path,
+                            OstreeSerializedVariantType *out_type,
+                            GVariant                   **out_variant,
+                            GError                     **error)
+{
+  GMappedFile *mfile = NULL;
+  gboolean ret = FALSE;
+  GVariant *ret_variant = NULL;
+  GVariant *container = NULL;
+  guint32 ret_type;
+
+  mfile = g_mapped_file_new (path, FALSE, error);
+  if (mfile == NULL)
+    {
+      goto out;
+    }
+  else
+    {
+      container = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT),
+                                           g_mapped_file_get_contents (mfile),
+                                           g_mapped_file_get_length (mfile),
+                                           FALSE,
+                                           (GDestroyNotify) g_mapped_file_unref,
+                                           mfile);
+      mfile = NULL;
+      g_variant_ref_sink (container);
+      g_variant_get (container, "(uv)",
+                     &ret_type, &ret_variant);
+      ret_type = GUINT32_FROM_BE (ret_type);
+      if (ret_type <= 0 || ret_type > OSTREE_SERIALIZED_VARIANT_LAST)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Corrupted metadata object '%s'; invalid type %d", path, ret_type);
+          goto out;
+        }
+    }
+
+  ret = TRUE;
+  *out_type = ret_type;
+  *out_variant = g_variant_ref_sink (ret_variant);
+  ret_variant = NULL;
+ out:
+  if (ret_variant)
+    g_variant_unref (ret_variant);
+  if (container != NULL)
+    g_variant_unref (container);
+  if (mfile != NULL)
+    g_mapped_file_unref (mfile);
+  return ret;
+}
+
+char *
+ostree_get_relative_object_path (const char *checksum,
+                                 OstreeObjectType type,
+                                 gboolean         archive)
+{
+  GString *path;
+  const char *type_string;
+
+  g_assert (strlen (checksum) == 64);
+
+  path = g_string_new ("objects/");
+
+  g_string_append_len (path, checksum, 2);
+  g_string_append_c (path, '/');
+  g_string_append (path, checksum + 2);
+  switch (type)
+    {
+    case OSTREE_OBJECT_TYPE_FILE:
+      if (archive)
+        type_string = ".packfile";
+      else
+        type_string = ".file";
+      break;
+    case OSTREE_OBJECT_TYPE_META:
+      type_string = ".meta";
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+  g_string_append (path, type_string);
+  return g_string_free (path, FALSE);
+}
+
+gboolean
+ostree_pack_object (GOutputStream     *output,
+                    GFile             *file,
+                    OstreeObjectType  objtype,
+                    GCancellable     *cancellable,
+                    GError          **error)
+{
+  gboolean ret = FALSE;
+  char *path = NULL;
+  GFileInfo *finfo = NULL;
+  GFileInputStream *instream = NULL;
+  gboolean pack_builder_initialized = FALSE;
+  GVariantBuilder pack_builder;
+  GVariant *pack_variant = NULL;
+  GVariant *xattrs = NULL;
+  gsize bytes_written;
+
+  path = g_file_get_path (file);
+
+  finfo = g_file_query_info (file, "standard::type,standard::size,standard::is-symlink,standard::symlink-target,unix::*",
+                             G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
+  if (!finfo)
+    goto out;
+
+  if (objtype == OSTREE_OBJECT_TYPE_META)
+    {
+      guint64 object_size_be = GUINT64_TO_BE ((guint64)g_file_info_get_size (finfo));
+      if (!g_output_stream_write_all (output, &object_size_be, 8, &bytes_written, cancellable, error))
+        goto out;
+
+      instream = g_file_read (file, NULL, error);
+      if (!instream)
+        goto out;
+      
+      if (g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error) < 0)
+        goto out;
+    }
+  else
+    {
+      guint32 uid, gid, mode;
+      guint32 device = 0;
+      guint32 metadata_size_be;
+      const char *target = NULL;
+      guint64 object_size;
+
+      uid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_UID);
+      gid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_GID);
+      mode = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_MODE);
+
+      g_variant_builder_init (&pack_builder, G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT));
+      pack_builder_initialized = TRUE;
+      g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (0));
+      g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (uid));
+      g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (gid));
+      g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (mode));
+
+      xattrs = ostree_get_xattrs_for_path (path, error);
+      if (!xattrs)
+        goto out;
+      g_variant_builder_add (&pack_builder, "@a(ayay)", xattrs);
+
+      if (S_ISREG (mode))
+        {
+          object_size = (guint64)g_file_info_get_size (finfo);
+        }
+      else if (S_ISLNK (mode))
+        {
+          target = g_file_info_get_attribute_byte_string (finfo, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
+          object_size = strlen (target);
+        }
+      else if (S_ISBLK (mode) || S_ISCHR (mode))
+        {
+          device = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_DEVICE);
+          object_size = 4;
+        }
+      else
+        g_assert_not_reached ();
+
+      g_variant_builder_add (&pack_builder, "t", GUINT64_TO_BE (object_size));
+      pack_variant = g_variant_builder_end (&pack_builder);
+      pack_builder_initialized = FALSE;
+
+      metadata_size_be = GUINT32_TO_BE (g_variant_get_size (pack_variant));
+
+      if (!g_output_stream_write_all (output, &metadata_size_be, 4,
+                                      &bytes_written, cancellable, error))
+        goto out;
+      g_assert (bytes_written == 4);
+
+      if (!g_output_stream_write_all (output, g_variant_get_data (pack_variant), g_variant_get_size (pack_variant),
+                                      &bytes_written, cancellable, error))
+        goto out;
+
+      if (S_ISREG (mode))
+        {
+          instream = g_file_read (file, NULL, error);
+          if (!instream)
+            goto out;
+          bytes_written = g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error);
+          if (bytes_written < 0)
+            goto out;
+          if (bytes_written != object_size)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "File size changed unexpectedly");
+              goto out;
+            }
+        }
+      else if (S_ISLNK (mode))
+        {
+          if (!g_output_stream_write_all (output, target, object_size,
+                                          &bytes_written, cancellable, error))
+            goto out;
+        }
+      else if (S_ISBLK (mode) || S_ISCHR (mode))
+        {
+          guint32 device_be = GUINT32_TO_BE (device);
+          g_assert (object_size == 4);
+          if (!g_output_stream_write_all (output, &device_be, object_size,
+                                          &bytes_written, cancellable, error))
+            goto out;
+          g_assert (bytes_written == 4);
+        }
+      else
+        g_assert_not_reached ();
+    }
+  
+  ret = TRUE;
+ out:
+  g_free (path);
+  g_clear_object (&finfo);
+  g_clear_object (&instream);
+  if (xattrs)
+    g_variant_unref (xattrs);
+  if (pack_builder_initialized)
+    g_variant_builder_clear (&pack_builder);
+  if (pack_variant)
+    g_variant_unref (pack_variant);
+  return ret;
+}
+
+static gboolean
+splice_and_checksum (GOutputStream  *out,
+                     GInputStream   *in,
+                     GChecksum      *checksum,
+                     GCancellable   *cancellable,
+                     GError        **error)
+{
+  gboolean ret = FALSE;
+  
+  if (checksum != NULL)
+    {
+      gsize bytes_read, bytes_written;
+      char buf[4096];
+      do
+        {
+          if (!g_input_stream_read_all (in, buf, sizeof(buf), &bytes_read, cancellable, error))
+            goto out;
+          if (checksum)
+            g_checksum_update (checksum, (guint8*)buf, bytes_read);
+          if (!g_output_stream_write_all (out, buf, bytes_read, &bytes_written, cancellable, error))
+            goto out;
+        }
+      while (bytes_read > 0);
+    }
+  else
+    {
+      if (g_output_stream_splice (out, in, 0, cancellable, error) < 0)
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+unpack_meta (const char   *path,
+             const char   *dest_path,    
+             GChecksum   **out_checksum,
+             GError      **error)
+{
+  gboolean ret = FALSE;
+  GFile *file = NULL;
+  GFile *dest_file = NULL;
+  GFileInputStream *in = NULL;
+  GChecksum *ret_checksum = NULL;
+  GFileOutputStream *out = NULL;
+
+  file = ot_util_new_file_for_path (path);
+  dest_file = ot_util_new_file_for_path (dest_path);
+
+  if (out_checksum)
+    ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+  in = g_file_read (file, NULL, error);
+  if (!in)
+    goto out;
+
+  out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
+  if (!out)
+    goto out;
+
+  if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error))
+    goto out;
+
+  if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+    goto out;
+
+  ret = TRUE;
+  if (out_checksum)
+    *out_checksum = ret_checksum;
+  ret_checksum = NULL;
+ out:
+  if (!ret)
+    (void) unlink (dest_path);
+  if (ret_checksum)
+    g_checksum_free (ret_checksum);
+  g_clear_object (&file);
+  g_clear_object (&dest_file);
+  g_clear_object (&in);
+  return ret;
+}
+
+
+static gboolean
+unpack_file (const char   *path,
+             const char   *dest_path,    
+             GChecksum   **out_checksum,
+             GError      **error)
+{
+  gboolean ret = FALSE;
+  GFile *file = NULL;
+  GFile *dest_file = NULL;
+  char *metadata_buf = NULL;
+  GVariant *metadata = NULL;
+  GVariant *xattrs = NULL;
+  GFileInputStream *in = NULL;
+  GFileOutputStream *out = NULL;
+  GChecksum *ret_checksum = NULL;
+  guint32 metadata_len;
+  guint32 version, uid, gid, mode;
+  guint64 content_len;
+  gsize bytes_read, bytes_written;
+  int temp_fd = -1;
+
+  file = ot_util_new_file_for_path (path);
+
+  in = g_file_read (file, NULL, error);
+  if (!in)
+    goto out;
+      
+  if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
+    goto out;
+  if (bytes_read != 4)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted packfile; too short while reading metadata length");
+      goto out;
+    }
+      
+  metadata_len = GUINT32_FROM_BE (metadata_len);
+  metadata_buf = g_malloc (metadata_len);
+
+  if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
+    goto out;
+  if (bytes_read != metadata_len)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted packfile; too short while reading metadata");
+      goto out;
+    }
+
+  metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
+                                      metadata_buf, metadata_len, FALSE, NULL, NULL);
+      
+  g_variant_get (metadata, "(uuuu@a(ayay)t)",
+                 &version, &uid, &gid, &mode,
+                 &xattrs, &content_len);
+  uid = GUINT32_FROM_BE (uid);
+  gid = GUINT32_FROM_BE (gid);
+  mode = GUINT32_FROM_BE (mode);
+  content_len = GUINT64_FROM_BE (content_len);
+
+  dest_file = ot_util_new_file_for_path (dest_path);
+      
+  if (out_checksum)
+    ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+  if (S_ISREG (mode))
+    {
+      out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
+      if (!out)
+        goto out;
+
+      if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error))
+        goto out;
+
+      if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+        goto out;
+    }
+  else if (S_ISLNK (mode))
+    {
+      char target[PATH_MAX+1];
+
+      if (!g_input_stream_read_all ((GInputStream*)in, target, sizeof(target)-1, &bytes_read, NULL, error))
+        goto out;
+      target[bytes_read] = '\0';
+      if (ret_checksum)
+        g_checksum_update (ret_checksum, (guint8*)target, bytes_read);
+      if (symlink (target, dest_path) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+  else if (S_ISCHR (mode) || S_ISBLK (mode))
+    {
+      guint32 dev;
+
+      if (!g_input_stream_read_all ((GInputStream*)in, &dev, 4, &bytes_read, NULL, error))
+        goto out;
+      if (bytes_read != 4)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Corrupted packfile; too short while reading device id");
+          goto out;
+        }
+      dev = GUINT32_FROM_BE (dev);
+      if (ret_checksum)
+        g_checksum_update (ret_checksum, (guint8*)&dev, 4);
+      if (mknod (dest_path, mode, dev) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+  else
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted packfile; invalid mode %u", mode);
+      goto out;
+    }
+
+  if (!S_ISLNK (mode))
+    {
+      if (chmod (dest_path, mode) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+
+  if (!ostree_set_xattrs (dest_path, xattrs, error))
+    goto out;
+
+  if (ret_checksum)
+    {
+      ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
+      g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+    }
+
+  ret = TRUE;
+  if (out_checksum)
+    *out_checksum = ret_checksum;
+  ret_checksum = NULL;
+ out:
+  if (!ret)
+    (void) unlink (dest_path);
+  if (ret_checksum)
+    g_checksum_free (ret_checksum);
+  g_free (metadata_buf);
+  g_clear_object (&file);
+  g_clear_object (&dest_file);
+  g_clear_object (&in);
+  g_clear_object (&out);
+  if (metadata)
+   g_variant_unref (metadata);
+  if (xattrs)
+    g_variant_unref (xattrs);
+  return ret;
+}
+
+gboolean
+ostree_unpack_object (const char   *path,
+                      OstreeObjectType  objtype,
+                      const char   *dest_path,    
+                      GChecksum   **out_checksum,
+                      GError      **error)
+{
+  if (objtype == OSTREE_OBJECT_TYPE_META)
+    return unpack_meta (path, dest_path, out_checksum, error);
+  else
+    return unpack_file (path, dest_path, out_checksum, error);
+}
+  
+
+
diff --git a/libostree/ostree-core.h b/libostree/ostree-core.h
new file mode 100644 (file)
index 0000000..c1fb491
--- /dev/null
@@ -0,0 +1,143 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_CORE
+#define _OSTREE_CORE
+
+#include <otutil.h>
+
+G_BEGIN_DECLS
+
+#define OSTREE_EMPTY_STRING_SHA256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
+
+typedef enum {
+  OSTREE_OBJECT_TYPE_FILE = 1,
+  OSTREE_OBJECT_TYPE_META = 2,
+} OstreeObjectType;
+
+typedef enum {
+  OSTREE_SERIALIZED_TREE_VARIANT = 1,
+  OSTREE_SERIALIZED_COMMIT_VARIANT = 2,
+  OSTREE_SERIALIZED_DIRMETA_VARIANT = 3,
+  OSTREE_SERIALIZED_XATTR_VARIANT = 4
+} OstreeSerializedVariantType;
+#define OSTREE_SERIALIZED_VARIANT_LAST 4
+
+#define OSTREE_SERIALIZED_VARIANT_FORMAT "(uv)"
+
+/*
+ * xattr objects:
+ * a(ayay) - array of (name, value) pairs, both binary data, though name is a bytestring
+ */
+#define OSTREE_XATTR_GVARIANT_FORMAT "a(ayay)"
+
+#define OSTREE_DIR_META_VERSION 0
+/*
+ * dirmeta objects:
+ * u - Version
+ * u - uid
+ * u - gid
+ * u - mode
+ * a(ayay) - xattrs
+ */
+#define OSTREE_DIRMETA_GVARIANT_FORMAT "(uuuua(ayay))"
+
+#define OSTREE_TREE_VERSION 0
+/*
+ * Tree objects:
+ * u - Version
+ * a{sv} - Metadata
+ * a(ss) - array of (filename, checksum) for files
+ * a(sss) - array of (dirname, tree_checksum, meta_checksum) for directories
+ */
+#define OSTREE_TREE_GVARIANT_FORMAT "(ua{sv}a(ss)a(sss)"
+
+#define OSTREE_COMMIT_VERSION 0
+/*
+ * Commit objects:
+ * u - Version
+ * a{sv} - Metadata
+ * s - parent checksum (empty string for initial)
+ * s - subject 
+ * s - body
+ * t - Timestamp in seconds since the epoch (UTC)
+ * s - Root tree contents
+ * s - Root tree metadata
+ */
+#define OSTREE_COMMIT_GVARIANT_FORMAT "(ua{sv}ssstss)"
+
+gboolean ostree_validate_checksum_string (const char *sha256,
+                                          GError    **error);
+
+char *ostree_get_relative_object_path (const char *checksum,
+                                       OstreeObjectType type,
+                                       gboolean         archive);
+
+GVariant *ostree_get_xattrs_for_path (const char   *path,
+                                      GError     **error);
+
+gboolean ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error);
+
+gboolean ostree_parse_metadata_file (const char                  *path,
+                                     OstreeSerializedVariantType *out_type,
+                                     GVariant                   **out_variant,
+                                     GError                     **error);
+
+gboolean ostree_stat_and_checksum_file (int dirfd, const char *path,
+                                        OstreeObjectType type,
+                                        GChecksum **out_checksum,
+                                        struct stat *out_stbuf,
+                                        GError **error);
+
+/* Packed files:
+ *
+ * guint32 metadata_length [metadata gvariant] [content]
+ *
+ * metadata variant:
+ * u - Version
+ * u - uid
+ * u - gid
+ * u - mode
+ * a(ayay) - xattrs
+ * t - content length
+ *
+ * And then following the end of the variant is the content.  If
+ * symlink, then this is the target; if device, then device ID as
+ * network byte order uint32.
+ */
+#define OSTREE_PACK_FILE_VARIANT_FORMAT "(uuuua(ayay)t)"
+
+gboolean  ostree_pack_object (GOutputStream     *output,
+                              GFile             *path,
+                              OstreeObjectType  objtype,
+                              GCancellable     *cancellable,
+                              GError          **error);
+
+gboolean ostree_unpack_object (const char   *path,
+                               OstreeObjectType  objtype,
+                               const char   *dest_path,    
+                               GChecksum   **out_checksum,
+                               GError      **error);
+
+void ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode);
+
+
+#endif /* _OSTREE_REPO */
diff --git a/libostree/ostree-repo.c b/libostree/ostree-repo.c
new file mode 100644 (file)
index 0000000..1b58d32
--- /dev/null
@@ -0,0 +1,2133 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+
+#include <gio/gunixoutputstream.h>
+#include <gio/gunixinputstream.h>
+
+static gboolean
+link_one_file (OstreeRepo *self, const char *path,
+               OstreeObjectType type,
+               gboolean ignore_exists, gboolean force,
+               GChecksum **out_checksum,
+               GError **error);
+static char *
+get_object_path (OstreeRepo  *self,
+                 const char    *checksum,
+                 OstreeObjectType type);
+
+enum {
+  PROP_0,
+
+  PROP_PATH
+};
+
+G_DEFINE_TYPE (OstreeRepo, ostree_repo, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_REPO, OstreeRepoPrivate))
+
+typedef struct _OstreeRepoPrivate OstreeRepoPrivate;
+
+struct _OstreeRepoPrivate {
+  char *path;
+  GFile *repo_file;
+  GFile *local_heads_dir;
+  GFile *remote_heads_dir;
+  char *objects_path;
+  char *config_path;
+
+  gboolean inited;
+
+  GKeyFile *config;
+  gboolean archive;
+};
+
+static void
+ostree_repo_finalize (GObject *object)
+{
+  OstreeRepo *self = OSTREE_REPO (object);
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+  g_free (priv->path);
+  g_clear_object (&priv->repo_file);
+  g_clear_object (&priv->local_heads_dir);
+  g_clear_object (&priv->remote_heads_dir);
+  g_free (priv->objects_path);
+  g_free (priv->config_path);
+  if (priv->config)
+    g_key_file_free (priv->config);
+
+  G_OBJECT_CLASS (ostree_repo_parent_class)->finalize (object);
+}
+
+static void
+ostree_repo_set_property(GObject         *object,
+                          guint            prop_id,
+                          const GValue    *value,
+                          GParamSpec      *pspec)
+{
+  OstreeRepo *self = OSTREE_REPO (object);
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+  switch (prop_id)
+    {
+    case PROP_PATH:
+      priv->path = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+ostree_repo_get_property(GObject         *object,
+                          guint            prop_id,
+                          GValue          *value,
+                          GParamSpec      *pspec)
+{
+  OstreeRepo *self = OSTREE_REPO (object);
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+  switch (prop_id)
+    {
+    case PROP_PATH:
+      g_value_set_string (value, priv->path);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static GObject *
+ostree_repo_constructor (GType                  gtype,
+                           guint                  n_properties,
+                           GObjectConstructParam *properties)
+{
+  GObject *object;
+  GObjectClass *parent_class;
+  OstreeRepoPrivate *priv;
+
+  parent_class = G_OBJECT_CLASS (ostree_repo_parent_class);
+  object = parent_class->constructor (gtype, n_properties, properties);
+
+  priv = GET_PRIVATE (object);
+
+  g_assert (priv->path != NULL);
+  
+  priv->repo_file = ot_util_new_file_for_path (priv->path);
+  priv->local_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/heads");
+  priv->remote_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/remotes");
+  
+  priv->objects_path = g_build_filename (priv->path, "objects", NULL);
+  priv->config_path = g_build_filename (priv->path, "config", NULL);
+
+  return object;
+}
+
+static void
+ostree_repo_class_init (OstreeRepoClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (OstreeRepoPrivate));
+
+  object_class->constructor = ostree_repo_constructor;
+  object_class->get_property = ostree_repo_get_property;
+  object_class->set_property = ostree_repo_set_property;
+  object_class->finalize = ostree_repo_finalize;
+
+  g_object_class_install_property (object_class,
+                                   PROP_PATH,
+                                   g_param_spec_string ("path",
+                                                        "",
+                                                        "",
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+ostree_repo_init (OstreeRepo *self)
+{
+}
+
+OstreeRepo*
+ostree_repo_new (const char *path)
+{
+  return g_object_new (OSTREE_TYPE_REPO, "path", path, NULL);
+}
+
+static gboolean
+parse_rev_file (OstreeRepo     *self,
+                const char     *path,
+                char          **sha256,
+                GError        **error) G_GNUC_UNUSED;
+
+static gboolean
+parse_rev_file (OstreeRepo     *self,
+                const char     *path,
+                char          **sha256,
+                GError        **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  GError *temp_error = NULL;
+  gboolean ret = FALSE;
+  char *rev = NULL;
+
+  rev = ot_util_get_file_contents_utf8 (path, &temp_error);
+  if (rev == NULL)
+    {
+      if (g_error_matches (temp_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+        {
+          g_clear_error (&temp_error);
+        }
+      else
+        {
+          g_propagate_error (error, temp_error);
+          goto out;
+        }
+    }
+  else
+    {
+      g_strchomp (rev);
+    }
+
+  if (g_str_has_prefix (rev, "ref: "))
+    {
+      GFile *ref;
+      char *ref_path;
+      char *ref_sha256;
+      gboolean subret;
+
+      ref = g_file_resolve_relative_path (priv->local_heads_dir, rev + 5);
+      ref_path = g_file_get_path (ref);
+
+      subret = parse_rev_file (self, ref_path, &ref_sha256, error);
+      g_clear_object (&ref);
+      g_free (ref_path);
+        
+      if (!subret)
+        {
+          g_free (ref_sha256);
+          goto out;
+        }
+      
+      g_free (rev);
+      rev = ref_sha256;
+    }
+  else 
+    {
+      if (!ostree_validate_checksum_string (rev, error))
+        goto out;
+    }
+
+  *sha256 = rev;
+  rev = NULL;
+  ret = TRUE;
+ out:
+  g_free (rev);
+  return ret;
+}
+
+static gboolean
+resolve_rev (OstreeRepo     *self,
+             const char     *rev,
+             gboolean        allow_noent,
+             char          **sha256,
+             GError        **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  char *tmp = NULL;
+  char *tmp2 = NULL;
+  char *ret_rev = NULL;
+  GFile *child = NULL;
+  char *child_path = NULL;
+  GError *temp_error = NULL;
+  GVariant *commit = NULL;
+
+  if (strlen (rev) == 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid empty rev");
+      goto out;
+    }
+  else if (strlen (rev) == 64)
+    {
+      ret_rev = g_strdup (rev);
+    }
+  else if (g_str_has_suffix (rev, "^"))
+    {
+      tmp = g_strdup (rev);
+      tmp[strlen(tmp) - 1] = '\0';
+
+      if (!resolve_rev (self, tmp, allow_noent, &tmp2, error))
+        goto out;
+
+      if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_COMMIT_VARIANT, tmp2, &commit, error))
+        goto out;
+      
+      g_variant_get_child (commit, 2, "s", &ret_rev);
+      if (strlen (ret_rev) == 0)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Commit %s has no parent", tmp2);
+          goto out;
+
+        }
+    }
+  else
+    {
+      child = g_file_get_child (priv->local_heads_dir, rev);
+      child_path = g_file_get_path (child);
+      if (!ot_util_gfile_load_contents_utf8 (child, NULL, &ret_rev, NULL, &temp_error))
+        {
+          if (allow_noent && g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+            {
+              g_free (ret_rev);
+              ret_rev = NULL;
+            }
+          else
+            {
+              g_propagate_error (error, temp_error);
+              g_prefix_error (error, "Couldn't open ref '%s': ", child_path);
+              goto out;
+            }
+        }
+      else
+        {
+          g_strchomp (ret_rev);
+         
+          if (!ostree_validate_checksum_string (ret_rev, error))
+            goto out;
+        }
+    }
+
+  *sha256 = ret_rev;
+  ret_rev = NULL;
+  ret = TRUE;
+ out:
+  if (commit)
+    g_variant_unref (commit);
+  g_free (tmp);
+  g_free (tmp2);
+  g_clear_object (&child);
+  g_free (child_path);
+  g_free (ret_rev);
+  return ret;
+}
+
+gboolean
+ostree_repo_resolve_rev (OstreeRepo     *self,
+                         const char     *rev,
+                         char          **sha256,
+                         GError        **error)
+{
+  return resolve_rev (self, rev, FALSE, sha256, error);
+}
+
+static gboolean
+write_checksum_file (GFile *parentdir,
+                     const char *name,
+                     const char *sha256,
+                     GError **error)
+{
+  gboolean ret = FALSE;
+  GFile *child = NULL;
+  GOutputStream *out = NULL;
+  gsize bytes_written;
+
+  child = g_file_get_child (parentdir, name);
+
+  if ((out = (GOutputStream*)g_file_replace (child, NULL, FALSE, 0, NULL, error)) == NULL)
+    goto out;
+  if (!g_output_stream_write_all (out, sha256, strlen (sha256), &bytes_written, NULL, error))
+    goto out;
+  if (!g_output_stream_write_all (out, "\n", 1, &bytes_written, NULL, error))
+    goto out;
+  if (!g_output_stream_close (out, NULL, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_clear_object (&child);
+  g_clear_object (&out);
+  return ret;
+}
+
+/**
+ * ostree_repo_get_config:
+ * @self:
+ *
+ * Returns: (transfer none): The repository configuration; do not modify
+ */
+GKeyFile *
+ostree_repo_get_config (OstreeRepo *self)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+  g_return_val_if_fail (priv->inited, NULL);
+
+  return priv->config;
+}
+
+/**
+ * ostree_repo_copy_config:
+ * @self:
+ *
+ * Returns: (transfer full): A newly-allocated copy of the repository config
+ */
+GKeyFile *
+ostree_repo_copy_config (OstreeRepo *self)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  GKeyFile *copy;
+  char *data;
+  gsize len;
+
+  g_return_val_if_fail (priv->inited, NULL);
+
+  copy = g_key_file_new ();
+  data = g_key_file_to_data (priv->config, &len, NULL);
+  if (!g_key_file_load_from_data (copy, data, len, 0, NULL))
+    g_assert_not_reached ();
+  g_free (data);
+  return copy;
+}
+
+/**
+ * ostree_repo_write_config:
+ * @self:
+ * @new_config: Overwrite the config file with this data.  Do not change later!
+ * @error: a #GError
+ *
+ * Save @new_config in place of this repository's config file.  Note
+ * that @new_config should not be modified after - this function
+ * simply adds a reference.
+ */
+gboolean
+ostree_repo_write_config (OstreeRepo *self,
+                          GKeyFile   *new_config,
+                          GError    **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  char *data = NULL;
+  gsize len;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (priv->inited, FALSE);
+
+  data = g_key_file_to_data (new_config, &len, error);
+  if (!g_file_set_contents (priv->config_path, data, len, error))
+    goto out;
+  
+  g_key_file_unref (priv->config);
+  priv->config = g_key_file_ref (new_config);
+
+  ret = TRUE;
+ out:
+  g_free (data);
+  return ret;
+}
+
+gboolean
+ostree_repo_check (OstreeRepo *self, GError **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  char *version = NULL;;
+  GError *temp_error = NULL;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  if (priv->inited)
+    return TRUE;
+
+  if (!g_file_test (priv->objects_path, G_FILE_TEST_IS_DIR))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Couldn't find objects directory '%s'", priv->objects_path);
+      goto out;
+    }
+  
+  priv->config = g_key_file_new ();
+  if (!g_key_file_load_from_file (priv->config, priv->config_path, 0, error))
+    {
+      g_prefix_error (error, "Couldn't parse config file: ");
+      goto out;
+    }
+
+  version = g_key_file_get_value (priv->config, "core", "repo_version", &temp_error);
+  if (temp_error)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+
+  if (strcmp (version, "0") != 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid repository version '%s'", version);
+      goto out;
+    }
+
+  priv->archive = g_key_file_get_boolean (priv->config, "core", "archive", &temp_error);
+  if (temp_error)
+    {
+      if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND))
+        {
+          g_clear_error (&temp_error);
+        }
+      else
+        {
+          g_propagate_error (error, temp_error);
+          goto out;
+        }
+    }
+
+  priv->inited = TRUE;
+  
+  ret = TRUE;
+ out:
+  g_free (version);
+  return ret;
+}
+
+const char *
+ostree_repo_get_path (OstreeRepo  *self)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  return priv->path;
+}
+
+gboolean      
+ostree_repo_is_archive (OstreeRepo  *self)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+  g_return_val_if_fail (priv->inited, FALSE);
+
+  return priv->archive;
+}
+
+static gboolean
+import_gvariant_object (OstreeRepo  *self,
+                        OstreeSerializedVariantType type,
+                        GVariant       *variant,
+                        GChecksum    **out_checksum,
+                        GError       **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  GVariant *serialized = NULL;
+  gboolean ret = FALSE;
+  gsize bytes_written;
+  char *tmp_name = NULL;
+  int fd = -1;
+  GUnixOutputStream *stream = NULL;
+
+  serialized = g_variant_new ("(uv)", GUINT32_TO_BE ((guint32)type), variant);
+
+  tmp_name = g_build_filename (priv->objects_path, "variant-tmp-XXXXXX", NULL);
+  fd = g_mkstemp (tmp_name);
+  if (fd < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  stream = (GUnixOutputStream*)g_unix_output_stream_new (fd, FALSE);
+  if (!g_output_stream_write_all ((GOutputStream*)stream,
+                                  g_variant_get_data (serialized),
+                                  g_variant_get_size (serialized),
+                                  &bytes_written,
+                                  NULL,
+                                  error))
+    goto out;
+  if (!g_output_stream_close ((GOutputStream*)stream,
+                              NULL, error))
+    goto out;
+
+  if (!link_one_file (self, tmp_name, OSTREE_OBJECT_TYPE_META, 
+                      TRUE, FALSE, out_checksum, error))
+    goto out;
+  
+  ret = TRUE;
+ out:
+  /* Unconditionally unlink; if we suceeded, there's a new link, if not, clean up. */
+  (void) unlink (tmp_name);
+  if (fd != -1)
+    close (fd);
+  if (serialized != NULL)
+    g_variant_unref (serialized);
+  g_free (tmp_name);
+  g_clear_object (&stream);
+  return ret;
+}
+
+gboolean
+ostree_repo_load_variant_checked (OstreeRepo  *self,
+                                  OstreeSerializedVariantType expected_type,
+                                  const char    *sha256, 
+                                  GVariant     **out_variant,
+                                  GError       **error)
+{
+  gboolean ret = FALSE;
+  OstreeSerializedVariantType type;
+  GVariant *ret_variant = NULL;
+
+  if (!ostree_repo_load_variant (self, sha256, &type, &ret_variant, error))
+    goto out;
+
+  if (type != expected_type)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted metadata object '%s'; found type %u, expected %u", sha256,
+                   type, (guint32)expected_type);
+      goto out;
+    }
+
+  ret = TRUE;
+  *out_variant = ret_variant;
+  ret_variant = NULL;
+ out:
+  if (ret_variant)
+    g_variant_unref (ret_variant);
+  return ret;
+}
+
+static gboolean
+import_directory_meta (OstreeRepo  *self,
+                       const char *path,
+                       GVariant  **out_variant,
+                       GChecksum **out_checksum,
+                       GError    **error)
+{
+  gboolean ret = FALSE;
+  struct stat stbuf;
+  GChecksum *ret_checksum = NULL;
+  GVariant *dirmeta = NULL;
+  GVariant *xattrs = NULL;
+
+  if (lstat (path, &stbuf) < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+  
+  if (!S_ISDIR(stbuf.st_mode))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Not a directory: '%s'", path);
+      goto out;
+    }
+
+  xattrs = ostree_get_xattrs_for_path (path, error);
+  if (!xattrs)
+    goto out;
+
+  dirmeta = g_variant_new ("(uuuu@a(ayay))",
+                           OSTREE_DIR_META_VERSION,
+                           GUINT32_TO_BE ((guint32)stbuf.st_uid),
+                           GUINT32_TO_BE ((guint32)stbuf.st_gid),
+                           GUINT32_TO_BE ((guint32)stbuf.st_mode),
+                           xattrs);
+  g_variant_ref_sink (dirmeta);
+
+  if (!import_gvariant_object (self, OSTREE_SERIALIZED_DIRMETA_VARIANT, 
+                               dirmeta, &ret_checksum, error))
+        goto out;
+
+  ret = TRUE;
+ out:
+  if (!ret)
+    {
+      if (ret_checksum)
+        g_checksum_free (ret_checksum);
+      if (dirmeta != NULL)
+        g_variant_unref (dirmeta);
+    }
+  else
+    {
+      *out_checksum = ret_checksum;
+      *out_variant = dirmeta;
+    }
+  if (xattrs)
+    g_variant_unref (xattrs);
+  return ret;
+}
+
+static char *
+get_object_path (OstreeRepo  *self,
+                 const char    *checksum,
+                 OstreeObjectType type)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  char *ret;
+  char *relpath;
+
+  relpath = ostree_get_relative_object_path (checksum, type, priv->archive);
+  ret = g_build_filename (priv->path, relpath, NULL);
+  g_free (relpath);
+  return ret;
+}
+
+static char *
+prepare_dir_for_checksum_get_object_path (OstreeRepo *self,
+                                          const char   *checksum,
+                                          OstreeObjectType type,
+                                          GError      **error)
+{
+  char *checksum_dir = NULL;
+  char *object_path = NULL;
+
+  object_path = get_object_path (self, checksum, type);
+  checksum_dir = g_path_get_dirname (object_path);
+
+  if (!ot_util_ensure_directory (checksum_dir, FALSE, error))
+    goto out;
+  
+ out:
+  g_free (checksum_dir);
+  return object_path;
+}
+
+static gboolean
+link_object_trusted (OstreeRepo   *self,
+                     const char   *path,
+                     const char   *checksum,
+                     OstreeObjectType objtype,
+                     gboolean      ignore_exists,
+                     gboolean      force,
+                     gboolean     *did_exist,
+                     GError      **error)
+{
+  char *src_basename = NULL;
+  char *src_dirname = NULL;
+  char *dest_basename = NULL;
+  char *tmp_dest_basename = NULL;
+  char *dest_dirname = NULL;
+  DIR *src_dir = NULL;
+  DIR *dest_dir = NULL;
+  gboolean ret = FALSE;
+  char *dest_path = NULL;
+
+  src_basename = g_path_get_basename (path);
+  src_dirname = g_path_get_dirname (path);
+
+  src_dir = opendir (src_dirname);
+  if (src_dir == NULL)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
+  if (!dest_path)
+    goto out;
+
+  dest_basename = g_path_get_basename (dest_path);
+  dest_dirname = g_path_get_dirname (dest_path);
+  dest_dir = opendir (dest_dirname);
+  if (dest_dir == NULL)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  if (force)
+    {
+      tmp_dest_basename = g_strconcat (dest_basename, ".tmp", NULL);
+      (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
+    }
+  else
+    tmp_dest_basename = g_strdup (dest_basename);
+  
+  if (linkat (dirfd (src_dir), src_basename, dirfd (dest_dir), tmp_dest_basename, 0) < 0)
+    {
+      if (errno != EEXIST || !ignore_exists)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+      else
+        *did_exist = TRUE;
+    }
+  else
+    *did_exist = FALSE;
+
+  if (force)
+    {
+      if (renameat (dirfd (dest_dir), tmp_dest_basename, 
+                    dirfd (dest_dir), dest_basename) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+      (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
+    }
+
+  ret = TRUE;
+ out:
+  if (src_dir != NULL)
+    closedir (src_dir);
+  if (dest_dir != NULL)
+    closedir (dest_dir);
+  g_free (src_basename);
+  g_free (src_dirname);
+  g_free (dest_basename);
+  g_free (tmp_dest_basename);
+  g_free (dest_dirname);
+  g_free (dest_path);
+  return ret;
+}
+
+static gboolean
+archive_file_trusted (OstreeRepo   *self,
+                      const char   *path,
+                      const char   *checksum,
+                      OstreeObjectType objtype,
+                      gboolean      ignore_exists,
+                      gboolean      force,
+                      gboolean     *did_exist,
+                      GError      **error)
+{
+  GFile *infile = NULL;
+  GFile *outfile = NULL;
+  GFileOutputStream *out = NULL;
+  gboolean ret = FALSE;
+  char *dest_path = NULL;
+  char *dest_tmp_path = NULL;
+
+  infile = ot_util_new_file_for_path (path);
+
+  dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
+  if (!dest_path)
+    goto out;
+
+  dest_tmp_path = g_strconcat (dest_path, ".tmp", NULL);
+
+  outfile = ot_util_new_file_for_path (dest_tmp_path);
+  out = g_file_replace (outfile, NULL, FALSE, 0, NULL, error);
+  if (!out)
+    goto out;
+
+  if (!ostree_pack_object ((GOutputStream*)out, infile, objtype, NULL, error))
+    goto out;
+  
+  if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+    goto out;
+
+  if (rename (dest_tmp_path, dest_path) < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  g_free (dest_path);
+  g_free (dest_tmp_path);
+  g_clear_object (&infile);
+  g_clear_object (&outfile);
+  g_clear_object (&out);
+  return ret;
+}
+  
+gboolean      
+ostree_repo_store_object_trusted (OstreeRepo   *self,
+                                  const char   *path,
+                                  const char   *checksum,
+                                  OstreeObjectType objtype,
+                                  gboolean      ignore_exists,
+                                  gboolean      force,
+                                  gboolean     *did_exist,
+                                  GError      **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  if (priv->archive && objtype == OSTREE_OBJECT_TYPE_FILE)
+    return archive_file_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
+  else
+    return link_object_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
+}
+
+static gboolean
+link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
+               gboolean ignore_exists, gboolean force,
+               GChecksum **out_checksum,
+               GError **error)
+{
+  gboolean ret = FALSE;
+  struct stat stbuf;
+  GChecksum *id = NULL;
+  gboolean did_exist;
+
+  if (!ostree_stat_and_checksum_file (-1, path, type, &id, &stbuf, error))
+    goto out;
+
+  if (!ostree_repo_store_object_trusted (self, path, g_checksum_get_string (id), type,
+                                         ignore_exists, force, &did_exist, error))
+    goto out;
+
+  *out_checksum = id;
+  id = NULL;
+  ret = TRUE;
+ out:
+  if (id != NULL)
+    g_checksum_free (id);
+  return ret;
+}
+
+gboolean
+ostree_repo_link_file (OstreeRepo *self,
+                       const char   *path,
+                       gboolean      ignore_exists,
+                       gboolean      force,
+                       GError      **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  GChecksum *checksum = NULL;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_return_val_if_fail (priv->inited, FALSE);
+
+  if (!link_one_file (self, path, OSTREE_OBJECT_TYPE_FILE,
+                      ignore_exists, force, &checksum, error))
+    return FALSE;
+  g_checksum_free (checksum);
+  return TRUE;
+}
+
+gboolean
+ostree_repo_store_packfile (OstreeRepo       *self,
+                            const char       *expected_checksum,
+                            const char       *path,
+                            OstreeObjectType  objtype,
+                            GError          **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  GString *tempfile_path = NULL;
+  GChecksum *checksum = NULL;
+  struct stat stbuf;
+  gboolean did_exist;
+
+  tempfile_path = g_string_new (priv->path);
+  g_string_append_printf (tempfile_path, "/tmp-unpack-%s", expected_checksum);
+  
+  if (!ostree_unpack_object (path, objtype, tempfile_path->str, &checksum, error))
+    goto out;
+
+  if (strcmp (g_checksum_get_string (checksum), expected_checksum) != 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted object %s (actual checksum is %s)",
+                   expected_checksum, g_checksum_get_string (checksum));
+      goto out;
+    }
+
+  if (!ostree_repo_store_object_trusted (self, tempfile_path ? tempfile_path->str : path,
+                                         expected_checksum,
+                                         objtype,
+                                         TRUE, FALSE, &did_exist, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  if (tempfile_path)
+    {
+      (void) unlink (tempfile_path->str);
+      g_string_free (tempfile_path, TRUE);
+    }
+  if (checksum)
+    g_checksum_free (checksum);
+  return ret;
+}
+
+typedef struct _ParsedTreeData ParsedTreeData;
+typedef struct _ParsedDirectoryData ParsedDirectoryData;
+
+static void parsed_tree_data_free (ParsedTreeData *pdata);
+
+struct _ParsedDirectoryData {
+  ParsedTreeData *tree_data;
+  char *metadata_sha256;
+  GVariant *meta_data;
+};
+
+static void
+parsed_directory_data_free (ParsedDirectoryData *pdata)
+{
+  if (pdata == NULL)
+    return;
+  parsed_tree_data_free (pdata->tree_data);
+  g_free (pdata->metadata_sha256);
+  g_variant_unref (pdata->meta_data);
+  g_free (pdata);
+}
+
+struct _ParsedTreeData {
+  GHashTable *files;  /* char* filename -> char* checksum */
+  GHashTable *directories;  /* char* dirname -> ParsedDirectoryData* */
+};
+
+static ParsedTreeData *
+parsed_tree_data_new (void)
+{
+  ParsedTreeData *ret = g_new0 (ParsedTreeData, 1);
+  ret->files = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                      (GDestroyNotify)g_free, 
+                                      (GDestroyNotify)g_free);
+  ret->directories = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                            (GDestroyNotify)g_free, 
+                                            (GDestroyNotify)parsed_directory_data_free);
+  return ret;
+}
+
+static void
+parsed_tree_data_free (ParsedTreeData *pdata)
+{
+  if (pdata == NULL)
+    return;
+  g_hash_table_destroy (pdata->files);
+  g_hash_table_destroy (pdata->directories);
+  g_free (pdata);
+}
+
+static gboolean
+parse_tree (OstreeRepo    *self,
+            const char      *sha256,
+            ParsedTreeData **out_pdata,
+            GError         **error)
+{
+  gboolean ret = FALSE;
+  ParsedTreeData *ret_pdata = NULL;
+  int i, n;
+  guint32 version;
+  GVariant *tree_variant = NULL;
+  GVariant *meta_variant = NULL;
+  GVariant *files_variant = NULL;
+  GVariant *dirs_variant = NULL;
+
+  if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_TREE_VARIANT,
+                                         sha256, &tree_variant, error))
+    goto out;
+
+  /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
+  g_variant_get (tree_variant, "(u@a{sv}@a(ss)@a(sss))",
+                 &version, &meta_variant, &files_variant, &dirs_variant);
+  version = GUINT32_FROM_BE (version);
+
+  ret_pdata = parsed_tree_data_new ();
+  n = g_variant_n_children (files_variant);
+  for (i = 0; i < n; i++)
+    {
+      const char *filename;
+      const char *checksum;
+
+      g_variant_get_child (files_variant, i, "(&s&s)", &filename, &checksum);
+
+      g_hash_table_insert (ret_pdata->files, g_strdup (filename), g_strdup (checksum));
+    }
+
+  n = g_variant_n_children (dirs_variant);
+  for (i = 0; i < n; i++)
+    {
+      const char *dirname;
+      const char *tree_checksum;
+      const char *meta_checksum;
+      ParsedTreeData *child_tree = NULL;
+      GVariant *metadata = NULL;
+      ParsedDirectoryData *child_dir = NULL;
+
+      g_variant_get_child (dirs_variant, i, "(&s&s&s)",
+                           &dirname, &tree_checksum, &meta_checksum);
+      
+      if (!parse_tree (self, tree_checksum, &child_tree, error))
+        goto out;
+
+      if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+                                             meta_checksum, &metadata, error))
+        {
+          parsed_tree_data_free (child_tree);
+          goto out;
+        }
+
+      child_dir = g_new0 (ParsedDirectoryData, 1);
+      child_dir->tree_data = child_tree;
+      child_dir->metadata_sha256 = g_strdup (meta_checksum);
+      child_dir->meta_data = metadata;
+
+      g_hash_table_insert (ret_pdata->directories, g_strdup (dirname), child_dir);
+    }
+
+  ret = TRUE;
+ out:
+  if (!ret)
+    parsed_tree_data_free (ret_pdata);
+  else
+    *out_pdata = ret_pdata;
+  if (tree_variant)
+    g_variant_unref (tree_variant);
+  if (meta_variant)
+    g_variant_unref (meta_variant);
+  if (files_variant)
+    g_variant_unref (files_variant);
+  if (dirs_variant)
+    g_variant_unref (dirs_variant);
+  return ret;
+}
+
+static gboolean
+load_commit_and_trees (OstreeRepo   *self,
+                       const char     *commit_sha256,
+                       GVariant      **out_commit,
+                       ParsedDirectoryData **out_root_data,
+                       GError        **error)
+{
+  GVariant *ret_commit = NULL;
+  ParsedDirectoryData *ret_root_data = NULL;
+  ParsedTreeData *tree_data = NULL;
+  char *ret_metadata_checksum = NULL;
+  GVariant *root_metadata = NULL;
+  gboolean ret = FALSE;
+  const char *tree_contents_checksum;
+  const char *tree_meta_checksum;
+
+  if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_COMMIT_VARIANT,
+                                         commit_sha256, &ret_commit, error))
+    goto out;
+
+  /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+  g_variant_get_child (ret_commit, 6, "&s", &tree_contents_checksum);
+  g_variant_get_child (ret_commit, 7, "&s", &tree_meta_checksum);
+
+  if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+                                         tree_meta_checksum, &root_metadata, error))
+    goto out;
+
+  if (!parse_tree (self, tree_contents_checksum, &tree_data, error))
+    goto out;
+
+  ret_root_data = g_new0 (ParsedDirectoryData, 1);
+  ret_root_data->tree_data = tree_data;
+  ret_root_data->metadata_sha256 = g_strdup (tree_meta_checksum);
+  ret_root_data->meta_data = root_metadata;
+  root_metadata = NULL;
+
+  ret = TRUE;
+  *out_commit = ret_commit;
+  ret_commit = NULL;
+  *out_root_data = ret_root_data;
+  ret_root_data = NULL;
+ out:
+  if (ret_commit)
+    g_variant_unref (ret_commit);
+  parsed_directory_data_free (ret_root_data);
+  g_free (ret_metadata_checksum);
+  if (root_metadata)
+    g_variant_unref (root_metadata);
+  return ret;
+}
+
+static GVariant *
+create_empty_gvariant_dict (void)
+{
+  GVariantBuilder builder;
+  g_variant_builder_init (&builder, G_VARIANT_TYPE("a{sv}"));
+  return g_variant_builder_end (&builder);
+}
+
+static gboolean
+import_parsed_tree (OstreeRepo    *self,
+                    ParsedTreeData  *tree,
+                    GChecksum      **out_checksum,
+                    GError         **error)
+{
+  gboolean ret = FALSE;
+  GVariant *serialized_tree = NULL;
+  gboolean builders_initialized = FALSE;
+  GVariantBuilder files_builder;
+  GVariantBuilder dirs_builder;
+  GHashTableIter hash_iter;
+  gpointer key, value;
+
+  g_variant_builder_init (&files_builder, G_VARIANT_TYPE ("a(ss)"));
+  g_variant_builder_init (&dirs_builder, G_VARIANT_TYPE ("a(sss)"));
+  builders_initialized = TRUE;
+
+  g_hash_table_iter_init (&hash_iter, tree->files);
+  while (g_hash_table_iter_next (&hash_iter, &key, &value))
+    {
+      const char *name = key;
+      const char *checksum = value;
+
+      g_variant_builder_add (&files_builder, "(ss)", name, checksum);
+    }
+
+  g_hash_table_iter_init (&hash_iter, tree->directories);
+  while (g_hash_table_iter_next (&hash_iter, &key, &value))
+    {
+      const char *name = key;
+      GChecksum *dir_checksum = NULL;
+      ParsedDirectoryData *dir = value;
+
+      if (!import_parsed_tree (self, dir->tree_data, &dir_checksum, error))
+        goto out;
+
+      g_variant_builder_add (&dirs_builder, "(sss)",
+                             name, g_checksum_get_string (dir_checksum), dir->metadata_sha256);
+      g_checksum_free (dir_checksum);
+    }
+
+  serialized_tree = g_variant_new ("(u@a{sv}@a(ss)@a(sss))",
+                                   GUINT32_TO_BE (0),
+                                   create_empty_gvariant_dict (),
+                                   g_variant_builder_end (&files_builder),
+                                   g_variant_builder_end (&dirs_builder));
+  builders_initialized = FALSE;
+  g_variant_ref_sink (serialized_tree);
+  if (!import_gvariant_object (self, OSTREE_SERIALIZED_TREE_VARIANT, serialized_tree, out_checksum, error))
+    goto out;
+  
+  ret = TRUE;
+ out:
+  if (builders_initialized)
+    {
+      g_variant_builder_clear (&files_builder);
+      g_variant_builder_clear (&dirs_builder);
+    }
+  if (serialized_tree)
+    g_variant_unref (serialized_tree);
+  return ret;
+}
+
+static gboolean
+check_path (const char *filename,
+            GError    **error)
+{
+  gboolean ret = FALSE;
+
+  if (!*filename)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid empty filename");
+      goto out;
+    }
+
+  if (strcmp (filename, ".") == 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Self-reference '.' in filename '%s' not allowed (yet)", filename);
+      goto out;
+    }
+  
+  if (ot_util_filename_has_dotdot (filename))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Path uplink '..' in filename '%s' not allowed (yet)", filename);
+      goto out;
+    }
+  
+  if (g_path_is_absolute (filename))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Absolute filename '%s' not allowed (yet)", filename);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+walk_parsed_tree (OstreeRepo  *self,
+                  const char    *filename,
+                  ParsedTreeData *tree,
+                  int            *out_filename_index, /* out*/
+                  char          **out_component, /* out, must free */
+                  ParsedTreeData **out_tree, /* out, but do not free */
+                  GError        **error)
+{
+  gboolean ret = FALSE;
+  GPtrArray *components = NULL;
+  ParsedTreeData *current_tree = tree;
+  const char *component = NULL;
+  const char *file_sha1 = NULL;
+  ParsedDirectoryData *dir = NULL;
+  int i;
+  int ret_filename_index = 0;
+
+  components = ot_util_path_split (filename);
+  g_assert (components != NULL);
+
+  current_tree = tree;
+  for (i = 0; i < components->len - 1; i++)
+    {
+      component = components->pdata[i];
+      file_sha1 = g_hash_table_lookup (current_tree->files, component);
+      dir = g_hash_table_lookup (current_tree->directories, component);
+
+      if (!(file_sha1 || dir))
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "No such file or directory: %s",
+                       filename);
+          goto out;
+        }
+      else if (file_sha1)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Encountered non-directory '%s' in '%s'",
+                       (char*)component,
+                       filename);
+          goto out;
+        }
+      else
+        {
+          g_assert (dir != NULL);
+          current_tree = dir->tree_data;
+          ret_filename_index++;
+        }
+    }
+
+  ret = TRUE;
+  *out_filename_index = i;
+  *out_component = components->pdata[components->len-1];
+  components->pdata[components->len-1] = NULL; /* steal */
+  *out_tree = current_tree;
+ out:
+  g_ptr_array_free (components, TRUE);
+  return ret;
+}
+
+static gboolean
+remove_files_from_tree (OstreeRepo   *self,
+                        const char     *base,
+                        GPtrArray      *removed_files,
+                        ParsedTreeData *tree,
+                        GError        **error)
+{
+  gboolean ret = FALSE;
+  int i;
+
+  for (i = 0; i < removed_files->len; i++)
+    {
+      const char *filename = removed_files->pdata[i];
+      int filename_index;
+      char *component = NULL;
+      ParsedTreeData *parent;
+      const char *file_sha1;
+      ParsedTreeData *dir;
+
+      if (!check_path (filename, error))
+        goto out;
+       
+      if (!walk_parsed_tree (self, filename, tree,
+                             &filename_index, (char**)&component, &parent,
+                             error))
+        goto out;
+
+      file_sha1 = g_hash_table_lookup (parent->files, component);
+      dir = g_hash_table_lookup (parent->directories, component);
+
+      if (file_sha1)
+        g_hash_table_remove (parent->files, component);
+      else if (dir)
+        g_hash_table_remove (parent->directories, component);
+      else
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "No such file or directory: %s",
+                       filename);
+          g_free (component);
+          goto out;
+        }
+      g_free (component);
+    }
+  
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+add_one_directory_to_tree_and_import (OstreeRepo   *self,
+                                      const char     *basename,
+                                      const char     *abspath,
+                                      ParsedTreeData *tree,
+                                      ParsedDirectoryData **dir, /*inout*/
+                                      GError        **error)
+{
+  gboolean ret = FALSE;
+  GVariant *dirmeta = NULL;
+  GChecksum *dir_meta_checksum = NULL;
+  ParsedDirectoryData *dir_value = *dir;
+  
+  g_assert (tree != NULL);
+
+  if (!import_directory_meta (self, abspath, &dirmeta, &dir_meta_checksum, error))
+    goto out;
+
+  if (dir_value)
+    {
+      g_variant_unref (dir_value->meta_data);
+      dir_value->meta_data = dirmeta;
+    }
+  else
+    {
+      dir_value = g_new0 (ParsedDirectoryData, 1);
+      dir_value->tree_data = parsed_tree_data_new ();
+      dir_value->metadata_sha256 = g_strdup (g_checksum_get_string (dir_meta_checksum));
+      dir_value->meta_data = dirmeta;
+      g_hash_table_insert (tree->directories, g_strdup (basename), dir_value);
+    }
+
+  ret = TRUE;
+  *dir = dir_value;
+ out:
+  if (dir_meta_checksum)
+    g_checksum_free (dir_meta_checksum);
+  return ret;
+}
+
+static gboolean
+add_one_file_to_tree_and_import (OstreeRepo   *self,
+                                 const char     *basename,
+                                 const char     *abspath,
+                                 ParsedTreeData *tree,
+                                 GError        **error)
+{
+  gboolean ret = FALSE;
+  GChecksum *checksum = NULL;
+  
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_assert (tree != NULL);
+
+  if (!link_one_file (self, abspath, OSTREE_OBJECT_TYPE_FILE,
+                      TRUE, FALSE, &checksum, error))
+    goto out;
+
+  g_hash_table_replace (tree->files, g_strdup (basename),
+                        g_strdup (g_checksum_get_string (checksum)));
+
+  ret = TRUE;
+ out:
+  if (checksum)
+    g_checksum_free (checksum);
+  return ret;
+}
+
+static gboolean
+add_one_path_to_tree_and_import (OstreeRepo     *self,
+                                 const char     *base,
+                                 const char     *filename,
+                                 ParsedTreeData *tree,
+                                 GError        **error)
+{
+  gboolean ret = FALSE;
+  GPtrArray *components = NULL;
+  struct stat stbuf;
+  char *component_abspath = NULL;
+  ParsedTreeData *current_tree = tree;
+  const char *component = NULL;
+  const char *file_sha1;
+  char *abspath = NULL;
+  ParsedDirectoryData *dir;
+  int i;
+  gboolean is_directory;
+
+  if (!check_path (filename, error))
+    goto out;
+
+  abspath = g_build_filename (base, filename, NULL);
+
+  if (lstat (abspath, &stbuf) < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+  is_directory = S_ISDIR(stbuf.st_mode);
+       
+  if (components)
+    g_ptr_array_free (components, TRUE);
+  components = ot_util_path_split (filename);
+  g_assert (components->len > 0);
+
+  current_tree = tree;
+  for (i = 0; i < components->len; i++)
+    {
+      component = components->pdata[i];
+      g_free (component_abspath);
+      component_abspath = ot_util_path_join_n (base, components, i);
+      file_sha1 = g_hash_table_lookup (current_tree->files, component);
+      dir = g_hash_table_lookup (current_tree->directories, component);
+
+      g_assert_cmpstr (component, !=, ".");
+
+      if (i < components->len - 1)
+        {
+          if (file_sha1 != NULL)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Encountered non-directory '%s' in '%s'",
+                           component,
+                           filename);
+              goto out;
+            }
+          /* Implicitly add intermediate directories */
+          if (!add_one_directory_to_tree_and_import (self, component,
+                                                     component_abspath, current_tree, &dir,
+                                                     error))
+            goto out;
+          g_assert (dir != NULL);
+          current_tree = dir->tree_data;
+        }
+      else if (is_directory)
+        {
+          if (file_sha1 != NULL)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "File '%s' can't be overwritten by directory",
+                           filename);
+              goto out;
+            }
+          if (!add_one_directory_to_tree_and_import (self, component,
+                                                     abspath, current_tree, &dir,
+                                                     error))
+            goto out;
+        }
+      else 
+        {
+          g_assert (!is_directory);
+          if (dir != NULL)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "File '%s' can't be overwritten by directory",
+                           filename);
+              goto out;
+            }
+          if (!add_one_file_to_tree_and_import (self, component, abspath,
+                                                current_tree, error))
+            goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  if (components)
+    g_ptr_array_unref (components);
+  g_free (component_abspath);
+  g_free (abspath);
+  return ret;
+}
+
+static gboolean
+add_files_to_tree_and_import (OstreeRepo   *self,
+                              const char     *base,
+                              GPtrArray      *added_files,
+                              ParsedTreeData *tree,
+                              GError        **error)
+{
+  gboolean ret = FALSE;
+  int i;
+
+  for (i = 0; i < added_files->len; i++)
+    {
+      const char *path = added_files->pdata[i];
+
+      if (!add_one_path_to_tree_and_import (self, base, path, tree, error))
+        goto out;
+    }
+  
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+gboolean      
+ostree_repo_write_ref (OstreeRepo  *self,
+                       gboolean     is_local,
+                       const char  *name,
+                       const char  *rev,
+                       GError     **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  return write_checksum_file (is_local ? priv->local_heads_dir : priv->remote_heads_dir, 
+                              name, rev, error);
+}
+
+static gboolean
+commit_parsed_tree (OstreeRepo *self,
+                    const char   *branch,
+                    const char   *parent,
+                    const char   *subject,
+                    const char   *body,
+                    GVariant     *metadata,
+                    ParsedDirectoryData *root,
+                    GChecksum   **out_commit,
+                    GError      **error)
+{
+  gboolean ret = FALSE;
+  GChecksum *root_checksum = NULL;
+  GChecksum *ret_commit = NULL;
+  GVariant *commit = NULL;
+  GDateTime *now = NULL;
+
+  g_assert (branch != NULL);
+  g_assert (subject != NULL);
+
+  if (!import_parsed_tree (self, root->tree_data, &root_checksum, error))
+    goto out;
+
+  now = g_date_time_new_now_utc ();
+  commit = g_variant_new ("(u@a{sv}ssstss)",
+                          GUINT32_TO_BE (OSTREE_COMMIT_VERSION),
+                          create_empty_gvariant_dict (),
+                          parent ? parent : "",
+                          subject, body ? body : "",
+                          GUINT64_TO_BE (g_date_time_to_unix (now)),
+                          g_checksum_get_string (root_checksum),
+                          root->metadata_sha256);
+  g_variant_ref_sink (commit);
+  if (!import_gvariant_object (self, OSTREE_SERIALIZED_COMMIT_VARIANT,
+                               commit, &ret_commit, error))
+    goto out;
+
+  if (!ostree_repo_write_ref (self, TRUE, branch, g_checksum_get_string (ret_commit), error))
+    goto out;
+
+  ret = TRUE;
+  *out_commit = ret_commit;
+ out:
+  if (root_checksum)
+    g_checksum_free (root_checksum);
+  if (commit)
+    g_variant_unref (commit);
+  if (now)
+    g_date_time_unref (now);
+  return ret;
+}
+
+static gboolean
+import_root (OstreeRepo           *self,
+             const char           *base,
+             ParsedDirectoryData **out_root,
+             GError              **error)
+{
+  gboolean ret = FALSE;
+  ParsedDirectoryData *ret_root = NULL;
+  GVariant *root_metadata = NULL;
+  GChecksum *root_meta_checksum = NULL;
+
+  if (!import_directory_meta (self, base, &root_metadata, &root_meta_checksum, error))
+    goto out;
+
+  ret_root = g_new0 (ParsedDirectoryData, 1);
+  ret_root->tree_data = parsed_tree_data_new ();
+  ret_root->meta_data = root_metadata;
+  root_metadata = NULL;
+  ret_root->metadata_sha256 = g_strdup (g_checksum_get_string (root_meta_checksum));
+
+  ret = TRUE;
+  *out_root = ret_root;
+  ret_root = NULL;
+ out:
+  if (root_metadata)
+    g_variant_unref (root_metadata);
+  if (root_meta_checksum)
+    g_checksum_free (root_meta_checksum);
+  parsed_directory_data_free (ret_root);
+  return ret;
+}
+
+gboolean
+ostree_repo_commit (OstreeRepo *self,
+                    const char   *branch,
+                    const char   *parent,
+                    const char   *subject,
+                    const char   *body,
+                    GVariant     *metadata,
+                    const char   *base,
+                    GPtrArray    *modified_files,
+                    GPtrArray    *removed_files,
+                    GChecksum   **out_commit,
+                    GError      **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  ParsedDirectoryData *root = NULL;
+  GVariant *previous_commit = NULL;
+  GChecksum *ret_commit_checksum = NULL;
+  GVariant *root_metadata = NULL;
+  GChecksum *root_meta_checksum = NULL;
+  char *current_head = NULL;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_return_val_if_fail (priv->inited, FALSE);
+  g_return_val_if_fail (branch != NULL, FALSE);
+  g_return_val_if_fail (subject != NULL, FALSE);
+
+  if (parent == NULL)
+    parent = branch;
+
+  if (!resolve_rev (self, parent, TRUE, &current_head, error))
+    goto out;
+
+  if (current_head)
+    {
+      if (!load_commit_and_trees (self, current_head, &previous_commit, &root, error))
+        goto out;
+      if (!import_directory_meta (self, base, &root_metadata, &root_meta_checksum, error))
+        goto out;
+      g_variant_unref (root->meta_data);
+      root->meta_data = root_metadata;
+      root_metadata = NULL;
+      root->metadata_sha256 = g_strdup (g_checksum_get_string (root_meta_checksum));
+    }
+  else
+    {
+      /* Initial commit */
+      if (!import_root (self, base, &root, error))
+        goto out;
+    }
+
+  if (!remove_files_from_tree (self, base, removed_files, root->tree_data, error))
+    goto out;
+
+  if (!add_files_to_tree_and_import (self, base, modified_files, root->tree_data, error))
+    goto out;
+
+  if (!commit_parsed_tree (self, branch, current_head,
+                           subject, body, metadata, root,
+                           &ret_commit_checksum, error))
+    goto out;
+  
+  ret = TRUE;
+  *out_commit = ret_commit_checksum;
+  ret_commit_checksum = NULL;
+ out:
+  if (ret_commit_checksum)
+    g_checksum_free (ret_commit_checksum);
+  g_free (current_head);
+  if (previous_commit)
+    g_variant_unref (previous_commit);
+  parsed_directory_data_free (root);
+  if (root_metadata)
+    g_variant_unref (root_metadata);
+  if (root_meta_checksum)
+    g_checksum_free (root_meta_checksum);
+  return ret;
+}
+
+gboolean      
+ostree_repo_commit_from_filelist_fd (OstreeRepo *self,
+                                     const char   *branch,
+                                     const char   *parent,
+                                     const char   *subject,
+                                     const char   *body,
+                                     GVariant     *metadata,
+                                     const char   *base,
+                                     int           fd,
+                                     char          separator,
+                                     GChecksum   **out_commit,
+                                     GError      **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  ParsedDirectoryData *root = NULL;
+  GChecksum *ret_commit_checksum = NULL;
+  GUnixInputStream *in = NULL;
+  GDataInputStream *datain = NULL;
+  char *filename = NULL;
+  gsize filename_len;
+  GError *temp_error = NULL;
+  GVariant *root_metadata = NULL;
+  GChecksum *root_meta_checksum = NULL;
+  char *current_head = NULL;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_return_val_if_fail (priv->inited, FALSE);
+  g_return_val_if_fail (branch != NULL, FALSE);
+  g_return_val_if_fail (subject != NULL, FALSE);
+
+  if (parent == NULL)
+    parent = branch;
+
+  /* We're overwriting the tree */
+  if (!import_root (self, base, &root, error))
+    goto out;
+
+  if (!resolve_rev (self, parent, TRUE, &current_head, error))
+    goto out;
+
+  in = (GUnixInputStream*)g_unix_input_stream_new (fd, FALSE);
+  datain = g_data_input_stream_new ((GInputStream*)in);
+
+  while ((filename = g_data_input_stream_read_upto (datain, &separator, 1,
+                                                    &filename_len, NULL, &temp_error)) != NULL)
+    {
+      if (!g_data_input_stream_read_byte (datain, NULL, &temp_error))
+        {
+          if (temp_error != NULL)
+            {
+              g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
+              goto out;
+            }
+        }
+      if (!add_one_path_to_tree_and_import (self, base, filename, root->tree_data, error))
+        goto out;
+      g_free (filename);
+      filename = NULL;
+    }
+  if (filename == NULL && temp_error != NULL)
+    {
+      g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
+      goto out;
+    }
+  if (!commit_parsed_tree (self, branch, current_head, subject, body, metadata,
+                           root, &ret_commit_checksum, error))
+    goto out;
+  
+  ret = TRUE;
+  *out_commit = ret_commit_checksum;
+  ret_commit_checksum = NULL;
+ out:
+  if (ret_commit_checksum)
+    g_checksum_free (ret_commit_checksum);
+  g_free (current_head);
+  if (root_metadata)
+    g_variant_unref (root_metadata);
+  if (root_meta_checksum)
+    g_checksum_free (root_meta_checksum);
+  g_clear_object (&datain);
+  g_clear_object (&in);
+  g_free (filename);
+  parsed_directory_data_free (root);
+  return ret;
+  
+}
+
+static gboolean
+iter_object_dir (OstreeRepo   *self,
+                 GFile          *dir,
+                 OstreeRepoObjectIter  callback,
+                 gpointer                user_data,
+                 GError                **error)
+{
+  gboolean ret = FALSE;
+  GError *temp_error = NULL;
+  GFileEnumerator *enumerator = NULL;
+  GFileInfo *file_info = NULL;
+  char *dirpath = NULL;
+
+  dirpath = g_file_get_path (dir);
+
+  enumerator = g_file_enumerate_children (dir, "standard::name,standard::type,unix::*", 
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          NULL, 
+                                          error);
+  if (!enumerator)
+    goto out;
+  
+  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+    {
+      const char *name;
+      guint32 type;
+      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
+      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+      
+      if (type != G_FILE_TYPE_DIRECTORY
+          && (g_str_has_suffix (name, ".meta")
+              || g_str_has_suffix (name, ".file")
+              || g_str_has_suffix (name, ".packfile")))
+        {
+          char *dot;
+          char *path;
+          
+          dot = strrchr (name, '.');
+          g_assert (dot);
+          
+          if ((dot - name) == 62)
+            {
+              path = g_build_filename (dirpath, name, NULL);
+              callback (self, path, file_info, user_data);
+              g_free (path);
+            }
+        }
+
+      g_object_unref (file_info);
+    }
+  if (file_info == NULL && temp_error != NULL)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+  if (!g_file_enumerator_close (enumerator, NULL, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_free (dirpath);
+  return ret;
+}
+
+gboolean
+ostree_repo_iter_objects (OstreeRepo  *self,
+                            OstreeRepoObjectIter callback,
+                            gpointer       user_data,
+                            GError        **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  GFile *objectdir = NULL;
+  GFileEnumerator *enumerator = NULL;
+  gboolean ret = FALSE;
+  GFileInfo *file_info = NULL;
+  GError *temp_error = NULL;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_return_val_if_fail (priv->inited, FALSE);
+
+  objectdir = ot_util_new_file_for_path (priv->objects_path);
+  enumerator = g_file_enumerate_children (objectdir, "standard::name,standard::type,unix::*", 
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          NULL, 
+                                          error);
+  if (!enumerator)
+    goto out;
+
+  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+    {
+      const char *name;
+      guint32 type;
+
+      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
+      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+      
+      if (strlen (name) == 2 && type == G_FILE_TYPE_DIRECTORY)
+        {
+          GFile *objdir = g_file_get_child (objectdir, name);
+          if (!iter_object_dir (self, objdir, callback, user_data, error))
+            {
+              g_object_unref (objdir);
+              goto out;
+            }
+          g_object_unref (objdir);
+        }
+      g_object_unref (file_info);
+    }
+  if (file_info == NULL && temp_error != NULL)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+  if (!g_file_enumerator_close (enumerator, NULL, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_clear_object (&file_info);
+  g_clear_object (&enumerator);
+  g_clear_object (&objectdir);
+  return ret;
+}
+
+gboolean
+ostree_repo_load_variant (OstreeRepo *self,
+                          const char   *sha256,
+                          OstreeSerializedVariantType *out_type,
+                          GVariant    **out_variant,
+                          GError      **error)
+{
+  gboolean ret = FALSE;
+  OstreeSerializedVariantType ret_type;
+  GVariant *ret_variant = NULL;
+  char *path = NULL;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  path = get_object_path (self, sha256, OSTREE_OBJECT_TYPE_META);
+  if (!ostree_parse_metadata_file (path, &ret_type, &ret_variant, error))
+    goto out;
+
+  ret = TRUE;
+  *out_type = ret_type;
+  *out_variant = ret_variant;
+  ret_variant = NULL;
+ out:
+  if (ret_variant)
+    g_variant_unref (ret_variant);
+  g_free (path);
+  return ret;
+}
+
+static gboolean
+checkout_tree (OstreeRepo    *self,
+               ParsedTreeData  *tree,
+               const char      *destination,
+               GError         **error);
+
+static gboolean
+checkout_one_directory (OstreeRepo  *self,
+                        const char *destination,
+                        const char *dirname,
+                        ParsedDirectoryData *dir,
+                        GError         **error)
+{
+  gboolean ret = FALSE;
+  char *dest_path = NULL;
+  guint32 version, uid, gid, mode;
+  GVariant *xattr_variant = NULL;
+
+  dest_path = g_build_filename (destination, dirname, NULL);
+      
+  /* PARSE OSTREE_SERIALIZED_DIRMETA_VARIANT */
+  g_variant_get (dir->meta_data, "(uuuu@a(ayay))",
+                 &version, &uid, &gid, &mode,
+                 &xattr_variant);
+  version = GUINT32_FROM_BE (version);
+  uid = GUINT32_FROM_BE (uid);
+  gid = GUINT32_FROM_BE (gid);
+  mode = GUINT32_FROM_BE (mode);
+
+  if (mkdir (dest_path, (mode_t)mode) < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      g_prefix_error (error, "Failed to create directory '%s': ", dest_path);
+      goto out;
+    }
+      
+  if (!checkout_tree (self, dir->tree_data, dest_path, error))
+    goto out;
+
+  if (!ostree_set_xattrs (dest_path, xattr_variant, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_free (dest_path);
+  g_variant_unref (xattr_variant);
+  return ret;
+}
+
+static gboolean
+checkout_tree (OstreeRepo    *self,
+               ParsedTreeData  *tree,
+               const char      *destination,
+               GError         **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  GHashTableIter hash_iter;
+  gpointer key, value;
+
+  g_hash_table_iter_init (&hash_iter, tree->files);
+  while (g_hash_table_iter_next (&hash_iter, &key, &value))
+    {
+      const char *filename = key;
+      const char *checksum = value;
+      char *object_path;
+      char *dest_path;
+
+      object_path = get_object_path (self, checksum, OSTREE_OBJECT_TYPE_FILE);
+      dest_path = g_build_filename (destination, filename, NULL);
+      
+      if (priv->archive)
+        {
+          if (!ostree_unpack_object (object_path, OSTREE_OBJECT_TYPE_FILE, dest_path, NULL, error))
+            goto out;
+        }
+      else
+        {
+          if (link (object_path, dest_path) < 0)
+            {
+              ot_util_set_error_from_errno (error, errno);
+              g_free (object_path);
+              g_free (dest_path);
+              goto out;
+            }
+          g_free (object_path);
+          g_free (dest_path);
+        }
+    }
+
+  g_hash_table_iter_init (&hash_iter, tree->directories);
+  while (g_hash_table_iter_next (&hash_iter, &key, &value))
+    {
+      const char *dirname = key;
+      ParsedDirectoryData *dir = value;
+      
+      if (!checkout_one_directory (self, destination, dirname, dir, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+gboolean
+ostree_repo_checkout (OstreeRepo *self,
+                      const char   *rev,
+                      const char   *destination,
+                      GError      **error)
+{
+  gboolean ret = FALSE;
+  GVariant *commit = NULL;
+  char *resolved = NULL;
+  char *root_meta_sha = NULL;
+  ParsedDirectoryData *root = NULL;
+
+  if (g_file_test (destination, G_FILE_TEST_EXISTS))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Destination path '%s' already exists",
+                   destination);
+      goto out;
+    }
+
+  if (!resolve_rev (self, rev, FALSE, &resolved, error))
+    goto out;
+
+  if (!load_commit_and_trees (self, resolved, &commit, &root, error))
+    goto out;
+
+  if (!checkout_one_directory (self, destination, NULL, root, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_free (resolved);
+  if (commit)
+    g_variant_unref (commit);
+  parsed_directory_data_free (root);
+  g_free (root_meta_sha);
+  return ret;
+}
diff --git a/libostree/ostree-repo.h b/libostree/ostree-repo.h
new file mode 100644 (file)
index 0000000..e9a690a
--- /dev/null
@@ -0,0 +1,151 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+/* ostree-repo.h */
+
+#ifndef _OSTREE_REPO
+#define _OSTREE_REPO
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO ostree_repo_get_type()
+#define OSTREE_REPO(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_REPO, OstreeRepo))
+#define OSTREE_REPO_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_REPO, OstreeRepoClass))
+#define OSTREE_IS_REPO(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_REPO))
+#define OSTREE_IS_REPO_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_REPO))
+#define OSTREE_REPO_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_REPO, OstreeRepoClass))
+
+typedef struct {
+  GObject parent;
+} OstreeRepo;
+
+typedef struct {
+  GObjectClass parent_class;
+} OstreeRepoClass;
+
+GType ostree_repo_get_type (void);
+
+OstreeRepo* ostree_repo_new (const char *path);
+
+gboolean      ostree_repo_check (OstreeRepo  *self, GError **error);
+
+const char *  ostree_repo_get_path (OstreeRepo  *self);
+
+gboolean      ostree_repo_is_archive (OstreeRepo  *self);
+
+GKeyFile *    ostree_repo_get_config (OstreeRepo *self);
+
+GKeyFile *    ostree_repo_copy_config (OstreeRepo *self);
+
+gboolean      ostree_repo_write_config (OstreeRepo *self,
+                                        GKeyFile   *new_config,
+                                        GError    **error);
+
+gboolean      ostree_repo_link_file (OstreeRepo *self,
+                                     const char   *path,
+                                     gboolean      ignore_exists,
+                                     gboolean      force,
+                                     GError      **error);
+
+gboolean      ostree_repo_store_packfile (OstreeRepo       *self,
+                                           const char       *expected_checksum,
+                                           const char       *path,
+                                           OstreeObjectType  objtype,
+                                           GError          **error);
+
+gboolean      ostree_repo_store_object_trusted (OstreeRepo   *self,
+                                                const char   *path,
+                                                const char   *checksum,
+                                                OstreeObjectType objtype,
+                                                gboolean      ignore_exists,
+                                                gboolean      force,
+                                                gboolean     *did_exist,
+                                                GError      **error);
+
+gboolean      ostree_repo_resolve_rev (OstreeRepo  *self,
+                                       const char  *rev,
+                                       char       **out_resolved,
+                                       GError     **error);
+
+gboolean      ostree_repo_write_ref (OstreeRepo  *self,
+                                     gboolean     is_local,
+                                     const char  *name,
+                                     const char  *rev,
+                                     GError     **error);
+
+gboolean      ostree_repo_load_variant (OstreeRepo *self,
+                                          const char   *sha256,
+                                          OstreeSerializedVariantType *out_type,
+                                          GVariant    **out_variant,
+                                          GError      **error);
+
+gboolean      ostree_repo_load_variant_checked (OstreeRepo  *self,
+                                                OstreeSerializedVariantType expected_type,
+                                                const char    *sha256, 
+                                                GVariant     **out_variant,
+                                                GError       **error);
+
+gboolean      ostree_repo_commit (OstreeRepo   *self,
+                                  const char   *branch,
+                                  const char   *parent,
+                                  const char   *subject,
+                                  const char   *body,
+                                  GVariant     *metadata,
+                                  const char   *base,
+                                  GPtrArray    *modified_files,
+                                  GPtrArray    *removed_files,
+                                  GChecksum   **out_commit,
+                                  GError      **error);
+
+gboolean      ostree_repo_commit_from_filelist_fd (OstreeRepo   *self,
+                                                   const char   *branch,
+                                                   const char   *parent,
+                                                   const char   *subject,
+                                                   const char   *body,
+                                                   GVariant     *metadata,
+                                                   const char   *base,
+                                                   int           fd,
+                                                   char          separator,
+                                                   GChecksum   **out_commit,
+                                                   GError      **error);
+
+gboolean      ostree_repo_checkout (OstreeRepo *self,
+                                      const char   *ref,
+                                      const char   *destination,
+                                      GError      **error);
+
+typedef void (*OstreeRepoObjectIter) (OstreeRepo *self, const char *path,
+                                        GFileInfo *fileinfo, gpointer user_data);
+
+gboolean     ostree_repo_iter_objects (OstreeRepo  *self,
+                                         OstreeRepoObjectIter callback,
+                                         gpointer       user_data,
+                                         GError        **error);
+
+G_END_DECLS
+
+#endif /* _OSTREE_REPO */
diff --git a/libostree/ostree.h b/libostree/ostree.h
new file mode 100644 (file)
index 0000000..f2b23d1
--- /dev/null
@@ -0,0 +1,28 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_H__
+
+#include <ostree-core.h>
+#include <ostree-repo.h>
+#include <ostree-checkout.h>
+
+#endif
diff --git a/libotutil/ot-gio-utils.c b/libotutil/ot-gio-utils.c
new file mode 100644 (file)
index 0000000..ad172de
--- /dev/null
@@ -0,0 +1,148 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <gio/gunixinputstream.h>
+
+#include <string.h>
+
+#include "otutil.h"
+
+gboolean
+ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error)
+{
+  GFile *dir;
+  GError *temp_error = NULL;
+  gboolean ret = FALSE;
+
+  dir = g_file_new_for_path (path);
+  if (with_parents)
+    ret = g_file_make_directory_with_parents (dir, NULL, &temp_error);
+  else
+    ret = g_file_make_directory (dir, NULL, &temp_error);
+  if (!ret)
+    {
+      if (!g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+        {
+          g_propagate_error (error, temp_error);
+          goto out;
+        }
+      else
+        g_clear_error (&temp_error);
+    }
+
+  ret = TRUE;
+ out:
+  g_clear_object (&dir);
+  return ret;
+}
+
+
+char *
+ot_util_get_file_contents_utf8 (const char *path,
+                                GError    **error)
+{
+  GFile *file = NULL;
+  char *ret = NULL;
+
+  file = ot_util_new_file_for_path (path);
+  if (!ot_util_gfile_load_contents_utf8 (file, NULL, &ret, NULL, error))
+    goto out;
+
+ out:
+  g_clear_object (&file);
+  return ret;
+}
+
+gboolean
+ot_util_gfile_load_contents_utf8 (GFile         *file,
+                                  GCancellable  *cancellable,
+                                  char         **contents_out,
+                                  char         **etag_out,
+                                  GError       **error)
+{
+  char *ret_contents = NULL;
+  char *ret_etag = NULL;
+  gsize len;
+  gboolean ret = FALSE;
+
+  if (!g_file_load_contents (file, cancellable, &ret_contents, &len, &ret_etag, error))
+    goto out;
+  if (!g_utf8_validate (ret_contents, len, NULL))
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_DATA,
+                   "Invalid UTF-8");
+      goto out;
+    }
+
+  if (contents_out)
+    *contents_out = ret_contents;
+  else
+    g_free (ret_contents);
+  ret_contents = NULL;
+  if (etag_out)
+    *etag_out = ret_etag;
+  else
+    g_free (ret_etag);
+  ret_etag = NULL;
+  ret = TRUE;
+ out:
+  g_free (ret_contents);
+  g_free (ret_etag);
+  return ret;
+}
+
+GInputStream *
+ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error)
+{
+  GInputStream *ret = NULL;
+  int fd;
+  int flags = O_RDONLY;
+  char *path = NULL;
+
+  path = g_file_get_path (file);
+#ifdef O_NOATIME
+  flags |= O_NOATIME;
+#endif
+  fd = open (path, flags);
+  if (fd < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  ret = (GInputStream*)g_unix_input_stream_new (fd, TRUE);
+  
+ out:
+  g_free (path);
+  return ret;
+}
+
+/* Like g_file_new_for_path, but only do local stuff, not GVFS */
+GFile *
+ot_util_new_file_for_path (const char *path)
+{
+  return g_vfs_get_file_for_path (g_vfs_get_local (), path);
+}
diff --git a/libotutil/ot-gio-utils.h b/libotutil/ot-gio-utils.h
new file mode 100644 (file)
index 0000000..0acb4dc
--- /dev/null
@@ -0,0 +1,45 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_GIO_UTILS_H__
+#define __OSTREE_GIO_UTILS_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+GFile *ot_util_new_file_for_path (const char *path);
+
+gboolean ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error);
+
+char * ot_util_get_file_contents_utf8 (const char *path, GError    **error);
+
+gboolean ot_util_gfile_load_contents_utf8 (GFile         *file,
+                                           GCancellable  *cancellable,
+                                           char         **contents_out,
+                                           char         **etag_out,
+                                           GError       **error);
+
+GInputStream *ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/libotutil/ot-opt-utils.c b/libotutil/ot-opt-utils.c
new file mode 100644 (file)
index 0000000..54d107c
--- /dev/null
@@ -0,0 +1,37 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "otutil.h"
+
+void
+ot_util_usage_error (GOptionContext *context, const char *message, GError **error)
+{
+  gchar *help = g_option_context_get_help (context, TRUE, NULL);
+  g_printerr ("%s\n", help);
+  g_free (help);
+  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message);
+}
diff --git a/libotutil/ot-opt-utils.h b/libotutil/ot-opt-utils.h
new file mode 100644 (file)
index 0000000..b65996f
--- /dev/null
@@ -0,0 +1,33 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_GIO_UTILS_H__
+#define __OSTREE_GIO_UTILS_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+void ot_util_usage_error (GOptionContext *context, const char *message, GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/libotutil/ot-unix-utils.c b/libotutil/ot-unix-utils.c
new file mode 100644 (file)
index 0000000..78243f7
--- /dev/null
@@ -0,0 +1,256 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-unix-utils.h"
+
+#include <gio/gio.h>
+#include <gio/gunixoutputstream.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+gboolean
+ot_util_spawn_pager (GOutputStream  **out_stream,
+                     GError         **error)
+{
+  const char *pager;
+  char *argv[2];
+  int stdin_fd;
+  pid_t pid;
+  gboolean ret = FALSE;
+  GOutputStream *ret_stream = NULL;
+
+  if (!isatty (1))
+    {
+      ret_stream = (GOutputStream*)g_unix_output_stream_new (1, TRUE);
+    }
+  else
+    {
+      pager = g_getenv ("GIT_PAGER");
+      if (pager == NULL)
+        pager = "less";
+      
+      argv[0] = (char*)pager;
+      argv[1] = NULL;
+      
+      if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+                                     NULL, NULL, &pid, &stdin_fd, NULL, NULL, error))
+        {
+          g_prefix_error (error, "%s", "Failed to spawn pager: ");
+          goto out;
+        }
+      
+      ret_stream = (GOutputStream*)g_unix_output_stream_new (stdin_fd, TRUE);
+    }
+
+  *out_stream = ret_stream;
+  ret_stream = NULL;
+  ret = TRUE;
+ out:
+  g_clear_object (&ret_stream);
+  return ret;
+}
+
+static int
+compare_filenames_by_component_length (const char *a,
+                                       const char *b)
+{
+  char *a_slash, *b_slash;
+
+  a_slash = strchr (a, '/');
+  b_slash = strchr (b, '/');
+  while (a_slash && b_slash)
+    {
+      a = a_slash + 1;
+      b = b_slash + 1;
+      a_slash = strchr (a, '/');
+      b_slash = strchr (b, '/');
+    }
+  if (a_slash)
+    return -1;
+  else if (b_slash)
+    return 1;
+  else
+    return 0;
+}
+
+GPtrArray *
+ot_util_sort_filenames_by_component_length (GPtrArray *files)
+{
+  GPtrArray *array = g_ptr_array_sized_new (files->len);
+  memcpy (array->pdata, files->pdata, sizeof (gpointer) * files->len);
+  g_ptr_array_sort (array, (GCompareFunc) compare_filenames_by_component_length);
+  return array;
+}
+
+int
+ot_util_count_filename_components (const char *path)
+{
+  int i = 0;
+
+  while (path)
+    {
+      i++;
+      path = strchr (path, '/');
+      if (path)
+        path++;
+    }
+  return i;
+}
+
+gboolean
+ot_util_filename_has_dotdot (const char *path)
+{
+  char *p;
+  char last;
+
+  if (strcmp (path, "..") == 0)
+    return TRUE;
+  if (g_str_has_prefix (path, "../"))
+    return TRUE;
+  p = strstr (path, "/..");
+  if (!p)
+    return FALSE;
+  last = *(p + 1);
+  return last == '\0' || last == '/';
+}
+
+GPtrArray *
+ot_util_path_split (const char *path)
+{
+  GPtrArray *ret = NULL;
+  const char *p;
+  const char *slash;
+  int i;
+
+  g_return_val_if_fail (path[0] != '/', NULL);
+
+  ret = g_ptr_array_new ();
+  g_ptr_array_set_free_func (ret, g_free);
+
+  p = path;
+  do {
+    slash = strchr (p, '/');
+    if (!slash)
+      {
+        g_ptr_array_add (ret, g_strdup (p));
+        p = NULL;
+      }
+    else
+      {
+        g_ptr_array_add (ret, g_strndup (p, slash - p));
+        p = slash + 1;
+      }
+  } while (p && *p);
+
+  /* Canonicalize by removing duplicate '.' */
+  for (i = ret->len-1; i >= 0; i--)
+    {
+      if (strcmp (ret->pdata[i], ".") == 0)
+        g_ptr_array_remove_index (ret, i);
+    }
+
+  return ret;
+}
+
+char *
+ot_util_path_join_n (const char *base, GPtrArray *components, int n)
+{
+  int max = MIN(n+1, components->len);
+  GPtrArray *subcomponents;
+  char *path;
+  int i;
+
+  subcomponents = g_ptr_array_new ();
+
+  if (base != NULL)
+    g_ptr_array_add (subcomponents, (char*)base);
+
+  for (i = 0; i < max; i++)
+    {
+      g_ptr_array_add (subcomponents, components->pdata[i]);
+    }
+  g_ptr_array_add (subcomponents, NULL);
+  
+  path = g_build_filenamev ((char**)subcomponents->pdata);
+  g_ptr_array_free (subcomponents, TRUE);
+  
+  return path;
+}
+
+void
+ot_util_set_error_from_errno (GError **error,
+                              gint     saved_errno)
+{
+  g_set_error_literal (error,
+                       G_IO_ERROR,
+                       0,
+                       g_strerror (saved_errno));
+  errno = saved_errno;
+}
+
+int
+ot_util_open_file_read (const char *path, GError **error)
+{
+  char *dirname = NULL;
+  char *basename = NULL;
+  DIR *dir = NULL;
+  int fd = -1;
+
+  dirname = g_path_get_dirname (path);
+  basename = g_path_get_basename (path);
+  dir = opendir (dirname);
+  if (dir == NULL)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  fd = ot_util_open_file_read_at (dirfd (dir), basename, error);
+
+ out:
+  g_free (basename);
+  g_free (dirname);
+  if (dir != NULL)
+    closedir (dir);
+  return fd;
+}
+
+int
+ot_util_open_file_read_at (int dirfd, const char *name, GError **error)
+{
+  int fd;
+  int flags = O_RDONLY;
+  
+#ifdef O_CLOEXEC
+  flags |= O_CLOEXEC;
+#endif
+#ifdef O_NOATIME
+  flags |= O_NOATIME;
+#endif
+  fd = openat (dirfd, name, flags);
+  if (fd < 0)
+    ot_util_set_error_from_errno (error, errno);
+  return fd;
+}
diff --git a/libotutil/ot-unix-utils.h b/libotutil/ot-unix-utils.h
new file mode 100644 (file)
index 0000000..0e1d392
--- /dev/null
@@ -0,0 +1,59 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_UNIX_UTILS_H__
+#define __OSTREE_UNIX_UTILS_H__
+
+#include <gio/gio.h>
+
+/* I just put all this shit here. Sue me. */
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+G_BEGIN_DECLS
+
+gboolean ot_util_spawn_pager (GOutputStream  **out_stream, GError         **error);
+
+gboolean ot_util_filename_has_dotdot (const char *path);
+
+GPtrArray *ot_util_sort_filenames_by_component_length (GPtrArray *files);
+
+GPtrArray* ot_util_path_split (const char *path);
+
+char *ot_util_path_join_n (const char *base, GPtrArray *components, int n);
+
+int ot_util_count_filename_components (const char *path);
+
+int ot_util_open_file_read (const char *path, GError **error);
+
+int ot_util_open_file_read_at (int dirfd, const char *name, GError **error);
+
+void ot_util_set_error_from_errno (GError **error, gint saved_errno);
+
+G_END_DECLS
+
+#endif
diff --git a/libotutil/otutil.h b/libotutil/otutil.h
new file mode 100644 (file)
index 0000000..2c49819
--- /dev/null
@@ -0,0 +1,28 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_UTIL_H__
+
+#include <ot-unix-utils.h>
+#include <ot-gio-utils.h>
+#include <ot-opt-utils.h>
+
+#endif
diff --git a/ostree/main.c b/ostree/main.c
new file mode 100644 (file)
index 0000000..ad5dfd8
--- /dev/null
@@ -0,0 +1,118 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "ot-builtins.h"
+
+static OstreeBuiltin builtins[] = {
+  { "checkout", ostree_builtin_checkout, 0 },
+  { "init", ostree_builtin_init, 0 },
+  { "commit", ostree_builtin_commit, 0 },
+  { "link-file", ostree_builtin_link_file, 0 },
+  { "log", ostree_builtin_log, 0 },
+  { "pull", ostree_builtin_pull, 0 },
+  { "fsck", ostree_builtin_fsck, 0 },
+  { "remote", ostree_builtin_remote, 0 },
+  { "rev-parse", ostree_builtin_rev_parse, 0 },
+  { "remote", ostree_builtin_remote, 0 },
+  { "run-triggers", ostree_builtin_run_triggers, 0 },
+  { "show", ostree_builtin_show, 0 },
+  { NULL }
+};
+
+static int
+usage (char **argv, gboolean is_error)
+{
+  OstreeBuiltin *builtin = builtins;
+  void (*print_func) (const gchar *format, ...);
+
+  if (is_error)
+    print_func = g_printerr;
+  else
+    print_func = g_print;
+
+  print_func ("usage: %s COMMAND [options]\n",
+              argv[0]);
+  print_func ("Builtin commands:\n");
+
+  while (builtin->name)
+    {
+      print_func ("  %s\n", builtin->name);
+      builtin++;
+    }
+  return (is_error ? 1 : 0);
+}
+
+
+int
+main (int    argc,
+      char **argv)
+{
+  OstreeBuiltin *builtin;
+  const char *cmd;
+
+  g_type_init ();
+
+  g_set_prgname (argv[0]);
+
+  builtin = builtins;
+
+  if (argc < 2)
+    return usage (argv, 1);
+  
+  cmd = argv[1];
+
+  while (builtin->name)
+    {
+      GError *error = NULL;
+      if (strcmp (cmd, builtin->name) == 0)
+        {
+          int i;
+          int tmp_argc;
+          char **tmp_argv;
+
+          tmp_argc = argc - 1;
+          tmp_argv = g_new0 (char *, tmp_argc + 1);
+
+          tmp_argv[0] = (char*)builtin->name;
+          for (i = 0; i < tmp_argc; i++)
+            tmp_argv[i+1] = argv[i+2];
+          if (!builtin->fn (tmp_argc, tmp_argv, NULL, &error))
+            {
+              g_free (tmp_argv);
+              g_printerr ("%s\n", error->message);
+              g_clear_error (&error);
+              return 1;
+            }
+          g_free (tmp_argv);
+          return 0;
+        }
+      builtin++;
+    }
+  
+  g_printerr ("Unknown command '%s'\n", cmd);
+  return usage (argv, 1);
+}
diff --git a/ostree/ot-builtin-checkout.c b/ostree/ot-builtin-checkout.c
new file mode 100644 (file)
index 0000000..30622fd
--- /dev/null
@@ -0,0 +1,82 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+  { NULL }
+};
+
+gboolean
+ostree_builtin_checkout (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  OstreeCheckout *checkout = NULL;
+  const char *commit;
+  const char *destination;
+
+  context = g_option_context_new ("COMMIT DESTINATION - Check out a commit into a filesystem tree");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 3)
+    {
+      gchar *help = g_option_context_get_help (context, TRUE, NULL);
+      g_printerr ("%s\n", help);
+      g_free (help);
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "COMMIT and DESTINATION must be specified");
+      goto out;
+    }
+
+  commit = argv[1];
+  destination = argv[2];
+
+  if (!ostree_repo_checkout (repo, commit, destination, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  g_clear_object (&checkout);
+  return ret;
+}
diff --git a/ostree/ot-builtin-commit.c b/ostree/ot-builtin-commit.c
new file mode 100644 (file)
index 0000000..19af39b
--- /dev/null
@@ -0,0 +1,176 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+static gboolean separator_null;
+static int from_fd = -1;
+static gboolean from_stdin;
+static char *from_file;
+static char *subject;
+static char *body;
+static char *parent;
+static char *branch;
+static char **additions;
+static char **removals;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+  { "subject", 's', 0, G_OPTION_ARG_STRING, &subject, "One line subject", "subject" },
+  { "body", 'm', 0, G_OPTION_ARG_STRING, &body, "Full description", "body" },
+  { "branch", 'b', 0, G_OPTION_ARG_STRING, &branch, "Branch", "branch" },
+  { "parent", 'p', 0, G_OPTION_ARG_STRING, &parent, "Parent commit", "commit" },
+  { "from-fd", 0, 0, G_OPTION_ARG_INT, &from_fd, "Read new tree files from fd", "file descriptor" },
+  { "from-stdin", 0, 0, G_OPTION_ARG_NONE, &from_stdin, "Read new tree files from stdin", "file descriptor" },
+  { "from-file", 0, 0, G_OPTION_ARG_FILENAME, &from_file, "Read new tree files from another file", "path" },
+  { "separator-null", 0, 0, G_OPTION_ARG_NONE, &separator_null, "", "Use '\\0' as filename separator, as with find -print0" },
+  { "add", 'a', 0, G_OPTION_ARG_FILENAME_ARRAY, &additions, "Relative file path to add", "filename" },
+  { "remove", 'r', 0, G_OPTION_ARG_FILENAME_ARRAY, &removals, "Relative file path to remove", "filename" },
+  { NULL }
+};
+
+gboolean
+ostree_builtin_commit (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  gboolean using_filename_cmdline;
+  gboolean using_filedescriptors;
+  GPtrArray *additions_array = NULL;
+  GPtrArray *removals_array = NULL;
+  GChecksum *commit_checksum = NULL;
+  char **iter;
+
+  context = g_option_context_new ("- Commit a new revision");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+  if (prefix == NULL)
+    prefix = ".";
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  using_filename_cmdline = (removals || additions);
+  using_filedescriptors = (from_file || from_fd >= 0 || from_stdin);
+
+  if (!(using_filename_cmdline || using_filedescriptors))
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "No additions or removals specified");
+      goto out;
+    }
+  if (using_filename_cmdline && using_filedescriptors)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "File descriptors may not be combined with --add or --remove");
+      goto out;
+    }
+
+  if (!branch)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "A branch must be specified with --branch");
+      goto out;
+    }
+
+  if (!subject)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "A subject must be specified with --subject");
+      goto out;
+    }
+
+  if (using_filename_cmdline)
+    {
+      g_assert (removals || additions);
+      additions_array = g_ptr_array_new ();
+      removals_array = g_ptr_array_new ();
+
+      if (additions)
+        for (iter = additions; *iter; iter++)
+          g_ptr_array_add (additions_array, *iter);
+      if (removals)
+        for (iter = removals; *iter; iter++)
+          g_ptr_array_add (removals_array, *iter);
+      
+      if (!ostree_repo_commit (repo, branch, parent, subject, body, NULL,
+                               prefix, additions_array,
+                               removals_array,
+                               &commit_checksum,
+                               error))
+        goto out;
+    }
+  else if (using_filedescriptors)
+    {
+      char separator = separator_null ? '\0' : '\n';
+      gboolean temp_fd = -1;
+
+      if (from_stdin)
+        from_fd = 0;
+      else if (from_file)
+        {
+          temp_fd = ot_util_open_file_read (from_file, error);
+          if (temp_fd < 0)
+            {
+              g_prefix_error (error, "Failed to open '%s': ", from_file);
+              goto out;
+            }
+          from_fd = temp_fd;
+        }
+      if (!ostree_repo_commit_from_filelist_fd (repo, branch, parent, subject, body, NULL,
+                                                prefix, from_fd, separator,
+                                                &commit_checksum, error))
+        {
+          if (temp_fd >= 0)
+            close (temp_fd);
+          goto out;
+        }
+      if (temp_fd >= 0)
+        close (temp_fd);
+    }
+  ret = TRUE;
+  g_print ("%s\n", g_checksum_get_string (commit_checksum));
+ out:
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  if (removals_array)
+    g_ptr_array_free (removals_array, TRUE);
+  if (additions_array)
+    g_ptr_array_free (additions_array, TRUE);
+  if (commit_checksum)
+    g_checksum_free (commit_checksum);
+  return ret;
+}
diff --git a/ostree/ot-builtin-fsck.c b/ostree/ot-builtin-fsck.c
new file mode 100644 (file)
index 0000000..3a21752
--- /dev/null
@@ -0,0 +1,229 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+static gboolean quiet;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", NULL },
+  { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
+  { NULL }
+};
+
+typedef struct {
+  guint n_objects;
+} HtFsckData;
+
+static gboolean
+checksum_packed_file (HtFsckData   *data,
+                      const char   *path,
+                      GChecksum   **out_checksum,
+                      GError      **error)
+{
+  gboolean ret = FALSE;
+  GChecksum *ret_checksum = NULL;
+  GFile *file = NULL;
+  char *metadata_buf = NULL;
+  GVariant *metadata = NULL;
+  GVariant *xattrs = NULL;
+  GFileInputStream *in = NULL;
+  guint32 metadata_len;
+  guint32 version, uid, gid, mode;
+  guint64 content_len;
+  gsize bytes_read;
+  char buf[8192];
+
+  file = ot_util_new_file_for_path (path);
+
+  in = g_file_read (file, NULL, error);
+  if (!in)
+    goto out;
+      
+  if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
+    goto out;
+      
+  metadata_len = GUINT32_FROM_BE (metadata_len);
+      
+  metadata_buf = g_malloc (metadata_len);
+      
+  if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
+    goto out;
+
+  metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
+                                      metadata_buf, metadata_len, FALSE, NULL, NULL);
+      
+  g_variant_get (metadata, "(uuuu@a(ayay)t)",
+                 &version, &uid, &gid, &mode,
+                 &xattrs, &content_len);
+  uid = GUINT32_FROM_BE (uid);
+  gid = GUINT32_FROM_BE (gid);
+  mode = GUINT32_FROM_BE (mode);
+  content_len = GUINT64_FROM_BE (content_len);
+
+  ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+  do
+    {
+      if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
+        goto out;
+      g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
+    }
+  while (bytes_read > 0);
+
+  ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
+  g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+
+  ret = TRUE;
+  *out_checksum = ret_checksum;
+  ret_checksum = NULL;
+ out:
+  if (ret_checksum)
+    g_checksum_free (ret_checksum);
+  g_free (metadata_buf);
+  g_clear_object (&file);
+  g_clear_object (&in);
+  if (metadata)
+   g_variant_unref (metadata);
+  if (xattrs)
+    g_variant_unref (xattrs);
+  return ret;
+}
+                    
+
+static void
+object_iter_callback (OstreeRepo  *repo,
+                      const char    *path,
+                      GFileInfo     *file_info,
+                      gpointer       user_data)
+{
+  HtFsckData *data = user_data;
+  struct stat stbuf;
+  GChecksum *checksum = NULL;
+  GError *error = NULL;
+  char *dirname = NULL;
+  char *checksum_prefix = NULL;
+  char *checksum_string = NULL;
+  char *filename_checksum = NULL;
+  gboolean packed = FALSE;
+  OstreeObjectType objtype;
+  char *dot;
+
+  /* nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
+     if (nlinks < 2 && !quiet)
+     g_printerr ("note: floating object: %s\n", path); */
+
+  if (g_str_has_suffix (path, ".meta"))
+    objtype = OSTREE_OBJECT_TYPE_META;
+  else if (g_str_has_suffix (path, ".file"))
+    objtype = OSTREE_OBJECT_TYPE_FILE;
+  else if (g_str_has_suffix (path, ".packfile"))
+    {
+      objtype = OSTREE_OBJECT_TYPE_FILE;
+     packed = TRUE;
+    }
+  else
+    g_assert_not_reached ();
+
+  if (packed && objtype == OSTREE_OBJECT_TYPE_FILE)
+    {
+      if (!checksum_packed_file (data, path, &checksum, &error))
+        goto out;
+    }
+  else
+    {
+      if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, &error))
+        goto out;
+    }
+
+  filename_checksum = g_strdup (g_file_info_get_name (file_info));
+  dot = strrchr (filename_checksum, '.');
+  g_assert (dot != NULL);
+  *dot = '\0';
+  
+  dirname = g_path_get_dirname (path);
+  checksum_prefix = g_path_get_basename (dirname);
+  checksum_string = g_strconcat (checksum_prefix, filename_checksum, NULL);
+  
+  if (strcmp (checksum_string, g_checksum_get_string (checksum)) != 0)
+    {
+      g_printerr ("ERROR: corrupted object '%s' expected checksum: %s\n",
+                  path, g_checksum_get_string (checksum));
+    }
+
+  data->n_objects++;
+
+ out:
+  if (checksum != NULL)
+    g_checksum_free (checksum);
+  g_free (dirname);
+  g_free (checksum_prefix);
+  g_free (checksum_string);
+  g_free (filename_checksum);
+  if (error != NULL)
+    {
+      g_printerr ("%s\n", error->message);
+      g_clear_error (&error);
+    }
+}
+
+gboolean
+ostree_builtin_fsck (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context;
+  HtFsckData data;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+
+  context = g_option_context_new ("- Check the repository for consistency");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+
+  data.n_objects = 0;
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (!ostree_repo_iter_objects (repo, object_iter_callback, &data, error))
+    goto out;
+
+  if (!quiet)
+    g_printerr ("Total Objects: %u\n", data.n_objects);
+
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  return ret;
+}
diff --git a/ostree/ot-builtin-init.c b/ostree/ot-builtin-init.c
new file mode 100644 (file)
index 0000000..d16b57c
--- /dev/null
@@ -0,0 +1,111 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+static gboolean archive;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", NULL },
+  { "archive", 0, 0, G_OPTION_ARG_NONE, &archive, "Initialize repository as archive", NULL },
+  { NULL }
+};
+
+#define DEFAULT_CONFIG_CONTENTS ("[core]\n" \
+                                 "repo_version=0\n")
+
+
+gboolean
+ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context = NULL;
+  gboolean ret = FALSE;
+  GFile *repodir = NULL;
+  GFile *child = NULL;
+  GFile *grandchild = NULL;
+  GString *config_data = NULL;
+
+  context = g_option_context_new ("- Initialize a new empty repository");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+
+  repodir = ot_util_new_file_for_path (repo_path);
+
+  child = g_file_get_child (repodir, "config");
+
+  config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
+  g_string_append_printf (config_data, "archive=%s\n", archive ? "true" : "false");
+  if (!g_file_replace_contents (child,
+                                config_data->str,
+                                config_data->len,
+                                NULL, FALSE, 0, NULL,
+                                NULL, error))
+    goto out;
+  g_clear_object (&child);
+
+  child = g_file_get_child (repodir, "objects");
+  if (!g_file_make_directory (child, NULL, error))
+    goto out;
+  g_clear_object (&child);
+
+  child = g_file_get_child (repodir, "refs");
+  if (!g_file_make_directory (child, NULL, error))
+    goto out;
+
+  grandchild = g_file_get_child (child, "heads");
+  if (!g_file_make_directory (grandchild, NULL, error))
+    goto out;
+  g_clear_object (&grandchild);
+
+  grandchild = g_file_get_child (child, "remotes");
+  if (!g_file_make_directory (grandchild, NULL, error))
+    goto out;
+  g_clear_object (&grandchild);
+
+  g_clear_object (&child);
+
+  child = g_file_get_child (repodir, "tags");
+  if (!g_file_make_directory (child, NULL, error))
+    goto out;
+  g_clear_object (&child);
+
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  if (config_data)
+    g_string_free (config_data, TRUE);
+  g_clear_object (&repodir);
+  g_clear_object (&child);
+  g_clear_object (&grandchild);
+  return ret;
+}
diff --git a/ostree/ot-builtin-link-file.c b/ostree/ot-builtin-link-file.c
new file mode 100644 (file)
index 0000000..dddec44
--- /dev/null
@@ -0,0 +1,73 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+static gboolean ignore_exists;
+static gboolean force;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+  { "ignore-exists", 'n', 0, G_OPTION_ARG_NONE, &ignore_exists, "Don't error if file exists", NULL },
+  { "force", 'f', 0, G_OPTION_ARG_NONE, &force, "If object exists, relink file", NULL },
+  { NULL }
+};
+
+gboolean
+ostree_builtin_link_file (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  int i;
+
+  context = g_option_context_new ("- Create a new hard link in the repository");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  for (i = 0; i < argc-1; i++)
+    {
+      if (!ostree_repo_link_file (repo, argv[i+1], ignore_exists, force, error))
+        goto out;
+    }
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  return ret;
+}
diff --git a/ostree/ot-builtin-log.c b/ostree/ot-builtin-log.c
new file mode 100644 (file)
index 0000000..c6d725b
--- /dev/null
@@ -0,0 +1,161 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+  { NULL }
+};
+
+gboolean
+ostree_builtin_log (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  GOutputStream *pager = NULL;
+  const char *rev;
+  GVariant *commit = NULL;
+  char *resolved_rev = NULL;
+
+  context = g_option_context_new ("- Show revision log");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+  if (prefix == NULL)
+    prefix = ".";
+
+  if (argc < 2)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "A revision must be specified");
+      goto out;
+    }
+                   
+  rev = argv[1];
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (!ot_util_spawn_pager (&pager, error))
+    goto out;
+
+  if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+    goto out;
+
+  while (TRUE)
+    {
+      OstreeSerializedVariantType type;
+      char *formatted = NULL;
+      guint32 version;
+      const char *parent;
+      const char *subject;
+      const char *body;
+      guint64 timestamp;
+      const char *contents;
+      const char *root_metadata;
+      GDateTime *time_obj = NULL;
+      char *formatted_date = NULL;
+      const char *body_newline;
+      gsize bytes_written;
+      GVariant *commit_metadata = NULL;
+      char *formatted_metadata = NULL;
+      
+      if (commit)
+        g_variant_unref (commit);
+      if (!ostree_repo_load_variant (repo, resolved_rev, &type, &commit, error))
+        goto out;
+
+      /* Ignore commit metadata for now */
+      g_variant_get (commit, "(u@a{sv}&s&s&st&s&s)",
+                     &version, &commit_metadata, &parent, &subject, &body,
+                     &timestamp, &contents, &root_metadata);
+      version = GUINT32_FROM_BE (version);
+      timestamp = GUINT64_FROM_BE (timestamp);
+      time_obj = g_date_time_new_from_unix_utc (timestamp);
+      formatted_date = g_date_time_format (time_obj, "%a %b %d %H:%M:%S %Y %z");
+      g_date_time_unref (time_obj);
+      time_obj = NULL;
+
+      formatted_metadata = g_variant_print (commit_metadata, TRUE);
+      g_variant_unref (commit_metadata);
+      formatted = g_strdup_printf ("commit %s\nSubject: %s\nDate: %s\nMetadata: %s\n\n",
+                                   resolved_rev, subject, formatted_date, formatted_metadata);
+      g_free (formatted_metadata);
+      g_free (formatted_date);
+      formatted_date = NULL;
+      
+      if (!g_output_stream_write_all (pager, formatted, strlen (formatted), &bytes_written, NULL, error))
+        {
+          g_free (formatted);
+          goto out;
+        }
+      g_free (formatted);
+      
+      body_newline = strchr (body, '\n');
+      do {
+        gsize len;
+        if (!g_output_stream_write_all (pager, "    ", 4, &bytes_written, NULL, error))
+          goto out;
+        len = body_newline ? body_newline - body : strlen (body);
+        if (!g_output_stream_write_all (pager, body, len, &bytes_written, NULL, error))
+          goto out;
+        if (!g_output_stream_write_all (pager, "\n\n", 2, &bytes_written, NULL, error))
+          goto out;
+        body_newline = strchr (body, '\n');
+        if (!body_newline)
+          break;
+        else
+          body_newline += 1;
+      } while (*body_newline);
+
+      if (strcmp (parent, "") == 0)
+        break;
+      g_free (resolved_rev);
+      resolved_rev = g_strdup (parent);
+    }
+
+  if (!g_output_stream_close (pager, NULL, error))
+    goto out;
+  ret = TRUE;
+ out:
+  g_free (resolved_rev);
+  if (context)
+    g_option_context_free (context);
+  if (commit)
+    g_variant_unref (commit);
+  g_clear_object (&repo);
+  return ret;
+}
diff --git a/ostree/ot-builtin-pull.c b/ostree/ot-builtin-pull.c
new file mode 100644 (file)
index 0000000..65a5206
--- /dev/null
@@ -0,0 +1,355 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+#include <libsoup/soup-gnome.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+  { NULL }
+};
+
+static gboolean
+fetch_uri (OstreeRepo  *repo,
+           SoupSession *soup,
+           SoupURI     *uri,
+           char       **temp_filename,
+           GError     **error)
+{
+  gboolean ret = FALSE;
+  SoupMessage *msg = NULL;
+  guint response;
+  char *template = NULL;
+  int fd;
+  SoupBuffer *buf = NULL;
+  GFile *tempf = NULL;
+  
+  msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+  
+  response = soup_session_send_message (soup, msg);
+  if (response != 200)
+    {
+      char *uri_string = soup_uri_to_string (uri, FALSE);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to retrieve '%s': %d %s",
+                   uri_string, response, msg->reason_phrase);
+      g_free (uri_string);
+      goto out;
+    }
+
+  template = g_strdup_printf ("%s/tmp-fetchXXXXXX", ostree_repo_get_path (repo));
+  
+  fd = g_mkstemp (template);
+  if (fd < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+  close (fd);
+  tempf = ot_util_new_file_for_path (template);
+
+  buf = soup_message_body_flatten (msg->response_body);
+
+  if (!g_file_replace_contents (tempf, buf->data, buf->length, NULL, FALSE, 0, NULL, NULL, error))
+    goto out;
+  
+  *temp_filename = template;
+  template = NULL;
+
+  ret = TRUE;
+ out:
+  g_free (template);
+  g_clear_object (&msg);
+  g_clear_object (&tempf);
+  return ret;
+}
+
+static gboolean
+store_object (OstreeRepo  *repo,
+              SoupSession *soup,
+              SoupURI     *baseuri,
+              const char  *object,
+              OstreeObjectType objtype,
+              gboolean    *did_exist,
+              GError     **error)
+{
+  gboolean ret = FALSE;
+  char *filename = NULL;
+  char *objpath = NULL;
+  char *relpath = NULL;
+  SoupURI *obj_uri = NULL;
+
+  objpath = ostree_get_relative_object_path (object, objtype, TRUE);
+  obj_uri = soup_uri_copy (baseuri);
+  relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL);
+  soup_uri_set_path (obj_uri, relpath);
+
+  if (!fetch_uri (repo, soup, obj_uri, &filename, error))
+    goto out;
+
+  if (!ostree_repo_store_packfile (repo, object, filename, objtype, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  if (obj_uri)
+    soup_uri_free (obj_uri);
+  if (filename)
+    (void) unlink (filename);
+  g_free (filename);
+  g_free (objpath);
+  g_free (relpath);
+  return ret;
+}
+
+static gboolean
+store_tree_recurse (OstreeRepo   *repo,
+                    SoupSession  *soup,
+                    SoupURI      *base_uri,
+                    const char   *rev,
+                    GError      **error)
+{
+  gboolean ret = FALSE;
+  GVariant *tree = NULL;
+  GVariant *files_variant = NULL;
+  GVariant *dirs_variant = NULL;
+  OstreeSerializedVariantType metatype;
+  gboolean did_exist;
+  int i, n;
+
+  if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+    goto out;
+
+  if (!did_exist)
+    {
+      if (!ostree_repo_load_variant (repo, rev, &metatype, &tree, error))
+        goto out;
+      
+      if (metatype != OSTREE_SERIALIZED_TREE_VARIANT)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Tree metadata '%s' has wrong type %d, expected %d",
+                       rev, metatype, OSTREE_SERIALIZED_TREE_VARIANT);
+          goto out;
+        }
+      
+      /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
+      g_variant_get_child (tree, 2, "@a(ss)", &files_variant);
+      g_variant_get_child (tree, 3, "@a(sss)", &dirs_variant);
+      
+      n = g_variant_n_children (files_variant);
+      for (i = 0; i < n; i++)
+        {
+          const char *filename;
+          const char *checksum;
+
+          g_variant_get_child (files_variant, i, "(ss)", &filename, &checksum);
+
+          if (!store_object (repo, soup, base_uri, checksum, OSTREE_OBJECT_TYPE_FILE, &did_exist, error))
+            goto out;
+        }
+      
+      for (i = 0; i < n; i++)
+        {
+          const char *dirname;
+          const char *tree_checksum;
+          const char *meta_checksum;
+
+          g_variant_get_child (dirs_variant, i, "(sss)",
+                               &dirname, &tree_checksum, &meta_checksum);
+
+          if (!store_tree_recurse (repo, soup, base_uri, tree_checksum, error))
+            goto out;
+
+          if (!store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+            goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  if (tree)
+    g_variant_unref (tree);
+  if (files_variant)
+    g_variant_unref (files_variant);
+  if (dirs_variant)
+    g_variant_unref (dirs_variant);
+  return ret;
+}
+
+static gboolean
+store_commit_recurse (OstreeRepo   *repo,
+                      SoupSession  *soup,
+                      SoupURI      *base_uri,
+                      const char   *rev,
+                      GError      **error)
+{
+  gboolean ret = FALSE;
+  GVariant *commit = NULL;
+  OstreeSerializedVariantType metatype;
+  const char *tree_contents_checksum;
+  const char *tree_meta_checksum;
+  gboolean did_exist;
+
+  if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+    goto out;
+
+  if (!did_exist)
+    {
+      if (!ostree_repo_load_variant (repo, rev, &metatype, &commit, error))
+        goto out;
+      
+      if (metatype != OSTREE_SERIALIZED_COMMIT_VARIANT)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Commit '%s' has wrong type %d, expected %d",
+                       rev, metatype, OSTREE_SERIALIZED_COMMIT_VARIANT);
+          goto out;
+        }
+      
+      /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+      g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
+      g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
+      
+      if (!store_object (repo, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+        goto out;
+      
+      if (!store_tree_recurse (repo, soup, base_uri, tree_contents_checksum, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  if (commit)
+    g_variant_unref (commit);
+  return ret;
+}
+                      
+gboolean
+ostree_builtin_pull (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  const char *remote;
+  const char *branch;
+  char *remote_branch_ref_path = NULL;
+  char *key = NULL;
+  char *baseurl = NULL;
+  char *refpath = NULL;
+  char *temppath = NULL;
+  GKeyFile *config = NULL;
+  SoupURI *base_uri = NULL;
+  SoupURI *target_uri = NULL;
+  SoupSession *soup = NULL;
+  char *rev = NULL;
+
+  context = g_option_context_new ("REMOTE BRANCH - Download data from remote repository");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 3)
+    {
+      ot_util_usage_error (context, "REMOTE and BRANCH must be specified", error);
+      goto out;
+    }
+
+  remote = argv[1];
+  branch = argv[2];
+
+  config = ostree_repo_get_config (repo);
+
+  key = g_strdup_printf ("remote \"%s\"", remote);
+  baseurl = g_key_file_get_string (config, key, "url", error);
+  if (!baseurl)
+    goto out;
+  base_uri = soup_uri_new (baseurl);
+  if (!base_uri)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to parse url '%s'", baseurl);
+      goto out;
+    }
+  target_uri = soup_uri_copy (base_uri);
+  g_free (refpath);
+  refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", branch, NULL);
+  soup_uri_set_path (target_uri, refpath);
+  
+  soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
+                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
+                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
+                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
+                                             NULL);
+  if (!fetch_uri (repo, soup, target_uri, &temppath, error))
+    goto out;
+
+  rev = ot_util_get_file_contents_utf8 (temppath, error);
+  if (!rev)
+    goto out;
+  g_strchomp (rev);
+
+  if (!ostree_validate_checksum_string (rev, error))
+    goto out;
+
+  if (!store_commit_recurse (repo, soup, base_uri, rev, error))
+    goto out;
+
+  if (!ostree_repo_write_ref (repo, FALSE, branch, rev, error))
+    goto out;
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  if (temppath)
+    (void) unlink (temppath);
+  g_free (temppath);
+  g_free (key);
+  g_free (rev);
+  g_free (baseurl);
+  g_free (refpath);
+  g_free (remote_branch_ref_path);
+  g_clear_object (&soup);
+  if (base_uri)
+    soup_uri_free (base_uri);
+  if (target_uri)
+    soup_uri_free (target_uri);
+  g_clear_object (&repo);
+  g_clear_object (&soup);
+  return ret;
+}
diff --git a/ostree/ot-builtin-remote.c b/ostree/ot-builtin-remote.c
new file mode 100644 (file)
index 0000000..40f7c29
--- /dev/null
@@ -0,0 +1,109 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+  { NULL }
+};
+
+static void
+usage_error (GOptionContext *context, const char *message, GError **error)
+{
+  gchar *help = g_option_context_get_help (context, TRUE, NULL);
+  g_printerr ("%s\n", help);
+  g_free (help);
+  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       message);
+}
+
+gboolean
+ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  OstreeCheckout *checkout = NULL;
+  const char *op;
+  GKeyFile *config = NULL;
+
+  context = g_option_context_new ("OPERATION [args] - Control remote repository configuration");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 2)
+    {
+      usage_error (context, "OPERATION must be specified", error);
+      goto out;
+    }
+
+  op = argv[1];
+
+  config = ostree_repo_copy_config (repo);
+
+  if (!strcmp (op, "add"))
+    {
+      char *key;
+      if (argc < 4)
+        {
+          usage_error (context, "NAME and URL must be specified", error);
+          goto out;
+        }
+      key = g_strdup_printf ("remote \"%s\"", argv[2]);
+      g_key_file_set_string (config, key, "url", argv[3]);
+      g_free (key);
+    }
+  else
+    {
+      usage_error (context, "Unknown operation", error);
+      goto out;
+    }
+
+  if (!ostree_repo_write_config (repo, config, error))
+    goto out;
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  if (config)
+    g_key_file_unref (config);
+  g_clear_object (&repo);
+  g_clear_object (&checkout);
+  return ret;
+}
diff --git a/ostree/ot-builtin-rev-parse.c b/ostree/ot-builtin-rev-parse.c
new file mode 100644 (file)
index 0000000..6e86d6c
--- /dev/null
@@ -0,0 +1,82 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+  { NULL }
+};
+
+gboolean
+ostree_builtin_rev_parse (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  const char *rev = "master";
+  char *resolved_rev = NULL;
+  GVariant *variant = NULL;
+  char *formatted_variant = NULL;
+
+  context = g_option_context_new ("REV - Output the target of a rev");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 2)
+    {
+      ot_util_usage_error (context, "REV must be specified", error);
+      goto out;
+    }
+  rev = argv[1];
+
+  if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+    goto out;
+
+  g_print ("%s\n", resolved_rev);
+  ret = TRUE;
+ out:
+  g_free (resolved_rev);
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  if (variant)
+    g_variant_unref (variant);
+  g_free (formatted_variant);
+  return ret;
+}
diff --git a/ostree/ot-builtin-run-triggers.c b/ostree/ot-builtin-run-triggers.c
new file mode 100644 (file)
index 0000000..4959156
--- /dev/null
@@ -0,0 +1,83 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+static gboolean quiet;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+  { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
+  { NULL }
+};
+
+gboolean
+ostree_builtin_run_triggers (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  OstreeCheckout *checkout = NULL;
+  const char *dir;
+
+  context = g_option_context_new ("DIR - Run trigger scripts for directory");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 1)
+    {
+      gchar *help = g_option_context_get_help (context, TRUE, NULL);
+      g_printerr ("%s\n", help);
+      g_free (help);
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "DIR must be specified");
+      goto out;
+    }
+
+  dir = argv[1];
+
+  checkout = ostree_checkout_new (repo, dir);
+  if (!ostree_checkout_run_triggers (checkout, error))
+    goto out;
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  g_clear_object (&checkout);
+  return ret;
+}
diff --git a/ostree/ot-builtin-show.c b/ostree/ot-builtin-show.c
new file mode 100644 (file)
index 0000000..8985c56
--- /dev/null
@@ -0,0 +1,84 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+  { NULL }
+};
+
+gboolean
+ostree_builtin_show (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  const char *rev = "master";
+  char *resolved_rev = NULL;
+  OstreeSerializedVariantType type;
+  GVariant *variant = NULL;
+  char *formatted_variant = NULL;
+
+  context = g_option_context_new ("- Output a metadata object");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc > 1)
+    rev = argv[1];
+
+  if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+    goto out;
+
+  if (!ostree_repo_load_variant (repo, resolved_rev, &type, &variant, error))
+    goto out;
+
+  g_print ("Object: %s\nType: %d\n", resolved_rev, type);
+  formatted_variant = g_variant_print (variant, TRUE);
+  g_print ("%s\n", formatted_variant);
+  ret = TRUE;
+ out:
+  g_free (resolved_rev);
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  if (variant)
+    g_variant_unref (variant);
+  g_free (formatted_variant);
+  return ret;
+}
diff --git a/ostree/ot-builtins.h b/ostree/ot-builtins.h
new file mode 100644 (file)
index 0000000..1373f60
--- /dev/null
@@ -0,0 +1,53 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_BUILTINS__
+#define __OSTREE_BUILTINS__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+  OSTREE_BUILTIN_FLAG_NONE = 0,
+} OstreeBuiltinFlags;
+
+typedef struct {
+  const char *name;
+  gboolean (*fn) (int argc, char **argv, const char *prefix, GError **error);
+  int flags; /* OstreeBuiltinFlags */
+} OstreeBuiltin;
+
+gboolean ostree_builtin_checkout (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_commit (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_log (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_link_file (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_pull (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_run_triggers (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_fsck (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_show (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_rev_parse (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libostree/ostree-checkout.c b/src/libostree/ostree-checkout.c
deleted file mode 100644 (file)
index 0569f28..0000000
+++ /dev/null
@@ -1,361 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ostree.h"
-#include "otutil.h"
-
-enum {
-  PROP_0,
-
-  PROP_REPO,
-  PROP_PATH
-};
-
-G_DEFINE_TYPE (OstreeCheckout, ostree_checkout, G_TYPE_OBJECT)
-
-#define GET_PRIVATE(o) \
-  (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_CHECKOUT, OstreeCheckoutPrivate))
-
-typedef struct _OstreeCheckoutPrivate OstreeCheckoutPrivate;
-
-struct _OstreeCheckoutPrivate {
-  OstreeRepo *repo;
-  char *path;
-};
-
-static void
-ostree_checkout_finalize (GObject *object)
-{
-  OstreeCheckout *self = OSTREE_CHECKOUT (object);
-  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
-
-  g_free (priv->path);
-  g_clear_object (&priv->repo);
-
-  G_OBJECT_CLASS (ostree_checkout_parent_class)->finalize (object);
-}
-
-static void
-ostree_checkout_set_property(GObject         *object,
-                          guint            prop_id,
-                          const GValue    *value,
-                          GParamSpec      *pspec)
-{
-  OstreeCheckout *self = OSTREE_CHECKOUT (object);
-  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
-
-  switch (prop_id)
-    {
-    case PROP_PATH:
-      priv->path = g_value_dup_string (value);
-      break;
-    case PROP_REPO:
-      priv->repo = g_value_dup_object (value);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static void
-ostree_checkout_get_property(GObject         *object,
-                          guint            prop_id,
-                          GValue          *value,
-                          GParamSpec      *pspec)
-{
-  OstreeCheckout *self = OSTREE_CHECKOUT (object);
-  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
-
-  switch (prop_id)
-    {
-    case PROP_PATH:
-      g_value_set_string (value, priv->path);
-      break;
-    case PROP_REPO:
-      g_value_set_object (value, priv->repo);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static GObject *
-ostree_checkout_constructor (GType                  gtype,
-                           guint                  n_properties,
-                           GObjectConstructParam *properties)
-{
-  GObject *object;
-  GObjectClass *parent_class;
-  OstreeCheckoutPrivate *priv;
-
-  parent_class = G_OBJECT_CLASS (ostree_checkout_parent_class);
-  object = parent_class->constructor (gtype, n_properties, properties);
-
-  priv = GET_PRIVATE (object);
-
-  g_assert (priv->path != NULL);
-  
-  return object;
-}
-
-static void
-ostree_checkout_class_init (OstreeCheckoutClass *klass)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  g_type_class_add_private (klass, sizeof (OstreeCheckoutPrivate));
-
-  object_class->constructor = ostree_checkout_constructor;
-  object_class->get_property = ostree_checkout_get_property;
-  object_class->set_property = ostree_checkout_set_property;
-  object_class->finalize = ostree_checkout_finalize;
-
-  g_object_class_install_property (object_class,
-                                   PROP_PATH,
-                                   g_param_spec_string ("path", "", "",
-                                                        NULL,
-                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
-  g_object_class_install_property (object_class,
-                                   PROP_REPO,
-                                   g_param_spec_object ("repo", "", "",
-                                                        OSTREE_TYPE_REPO,
-                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-}
-
-static void
-ostree_checkout_init (OstreeCheckout *self)
-{
-}
-
-OstreeCheckout*
-ostree_checkout_new (OstreeRepo  *repo,
-                     const char  *path)
-{
-  return g_object_new (OSTREE_TYPE_CHECKOUT, "repo", repo, "path", path, NULL);
-}
-
-static gboolean
-executable_exists_in_checkout (const char *path,
-                               const char *executable)
-{
-  int i;
-  const char *subdirs[] = {"bin", "sbin", "usr/bin", "usr/sbin"};
-
-  for (i = 0; i < G_N_ELEMENTS (subdirs); i++)
-    {
-      char *possible_path = g_build_filename (path, subdirs[i], executable, NULL);
-      gboolean exists;
-      
-      exists = g_file_test (possible_path, G_FILE_TEST_EXISTS);
-      g_free (possible_path);
-
-      if (exists)
-        return TRUE;
-    }
-
-  return FALSE;
-}
-
-static gboolean
-run_trigger (OstreeCheckout *self,
-             GFile          *trigger,
-             gboolean        requires_chroot,
-             GError        **error)
-{
-  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
-  gboolean ret = FALSE;
-  char *path = NULL;
-  char *temp_path = NULL;
-  char *rel_temp_path = NULL;
-  GFile *temp_copy = NULL;
-  char *basename = NULL;
-  GPtrArray *args = NULL;
-  int estatus;
-
-  path = g_file_get_path (trigger);
-  basename = g_path_get_basename (path);
-
-  args = g_ptr_array_new ();
-  
-  if (requires_chroot)
-    {
-      temp_path = g_build_filename (priv->path, basename, NULL);
-      rel_temp_path = g_strconcat ("./", basename, NULL);
-      temp_copy = ot_util_new_file_for_path (temp_path);
-
-      if (!g_file_copy (trigger, temp_copy, 0, NULL, NULL, NULL, error))
-        goto out;
-
-      g_ptr_array_add (args, "chroot");
-      g_ptr_array_add (args, ".");
-      g_ptr_array_add (args, rel_temp_path);
-      g_ptr_array_add (args, NULL);
-    }
-  else
-    {
-      g_ptr_array_add (args, path);
-      g_ptr_array_add (args, NULL);
-    }
-      
-  g_print ("Running trigger: %s\n", path);
-  if (!g_spawn_sync (priv->path,
-                     (char**)args->pdata,
-                     NULL,
-                     G_SPAWN_SEARCH_PATH,
-                     NULL, NULL, NULL, NULL,
-                     &estatus,
-                     error))
-    {
-      g_prefix_error (error, "Failed to run trigger %s: ", basename);
-      goto out;
-    }
-
-  ret = TRUE;
- out:
-  if (requires_chroot && temp_path)
-    (void)unlink (temp_path);
-    
-  g_free (path);
-  g_free (basename);
-  g_free (temp_path);
-  g_free (rel_temp_path);
-  g_clear_object (&temp_copy);
-  if (args)
-    g_ptr_array_free (args, TRUE);
-  return ret;
-}
-
-static gboolean
-check_trigger (OstreeCheckout *self,
-               GFile          *trigger,
-               GError        **error)
-{
-  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
-  gboolean ret = FALSE;
-  GInputStream *instream = NULL;
-  GDataInputStream *datain = NULL;
-  GError *temp_error = NULL;
-  char *line;
-  gsize len;
-  gboolean requires_chroot = TRUE;
-  gboolean matches = FALSE;
-
-  instream = (GInputStream*)g_file_read (trigger, NULL, error);
-  if (!instream)
-    goto out;
-  datain = g_data_input_stream_new (instream);
-
-  while ((line = g_data_input_stream_read_line (datain, &len, NULL, &temp_error)) != NULL)
-    {
-      if (g_str_has_prefix (line, "# IfExecutable: "))
-        {
-          char *executable = g_strdup (line + strlen ("# IfExecutable: "));
-          g_strchomp (executable);
-          matches = executable_exists_in_checkout (priv->path, executable);
-          g_free (executable);
-        }
-
-      g_free (line);
-    }
-  if (line == NULL && temp_error != NULL)
-    {
-      g_propagate_error (error, temp_error);
-      goto out;
-    }
-  if (matches)
-    {
-      if (!run_trigger (self, trigger, requires_chroot, error))
-        goto out;
-    }
-  
-  ret = TRUE;
- out:
-  g_clear_object (&instream);
-  g_clear_object (&datain);
-  return ret;
-}
-
-gboolean
-ostree_checkout_run_triggers (OstreeCheckout *self,
-                              GError        **error)
-{
-  gboolean ret = FALSE;
-  GError *temp_error = NULL;
-  char *triggerdir_path = NULL;
-  GFile *triggerdir = NULL;
-  GFileInfo *file_info = NULL;
-  GFileEnumerator *enumerator = NULL;
-
-  triggerdir_path = g_build_filename (LIBEXECDIR, "ostree", "triggers.d", NULL);
-  triggerdir = ot_util_new_file_for_path (triggerdir_path);
-
-  enumerator = g_file_enumerate_children (triggerdir, "standard::name,standard::type,unix::*", 
-                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                          NULL, 
-                                          error);
-  if (!enumerator)
-    goto out;
-
-  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
-    {
-      const char *name;
-      guint32 type;
-      char *child_path = NULL;
-      GFile *child = NULL;
-      gboolean success;
-
-      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
-      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
-      
-      if (type == G_FILE_TYPE_REGULAR && g_str_has_suffix (name, ".trigger"))
-        {
-          child_path = g_build_filename (triggerdir_path, name, NULL);
-          child = ot_util_new_file_for_path (child_path);
-
-          success = check_trigger (self, child, error);
-        }
-      else
-        success = TRUE;
-
-      g_object_unref (file_info);
-      g_free (child_path);
-      g_clear_object (&child);
-      if (!success)
-        goto out;
-    }
-  if (file_info == NULL && temp_error != NULL)
-    {
-      g_propagate_error (error, temp_error);
-      goto out;
-    }
-
-  ret = TRUE;
- out:
-  g_free (triggerdir_path);
-  g_clear_object (&triggerdir);
-  g_clear_object (&enumerator);
-  return ret;
-}
diff --git a/src/libostree/ostree-checkout.h b/src/libostree/ostree-checkout.h
deleted file mode 100644 (file)
index 375b977..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef _OSTREE_CHECKOUT
-#define _OSTREE_CHECKOUT
-
-#include <ostree-repo.h>
-
-G_BEGIN_DECLS
-
-#define OSTREE_TYPE_CHECKOUT ostree_checkout_get_type()
-#define OSTREE_CHECKOUT(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckout))
-#define OSTREE_CHECKOUT_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
-#define OSTREE_IS_CHECKOUT(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_CHECKOUT))
-#define OSTREE_IS_CHECKOUT_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_CHECKOUT))
-#define OSTREE_CHECKOUT_GET_CLASS(obj) \
-  (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
-
-typedef struct {
-  GObject parent;
-} OstreeCheckout;
-
-typedef struct {
-  GObjectClass parent_class;
-} OstreeCheckoutClass;
-
-GType ostree_checkout_get_type (void);
-
-OstreeCheckout* ostree_checkout_new (OstreeRepo  *repo,
-                                     const char  *path);
-
-gboolean ostree_checkout_run_triggers (OstreeCheckout *checkout,
-                                       GError        **error);
-
-G_END_DECLS
-
-#endif /* _OSTREE_CHECKOUT */
diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c
deleted file mode 100644 (file)
index d9027a3..0000000
+++ /dev/null
@@ -1,814 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ostree.h"
-#include "otutil.h"
-
-#include <sys/types.h>
-#include <attr/xattr.h>
-
-gboolean
-ostree_validate_checksum_string (const char *sha256,
-                                 GError    **error)
-{
-  if (strlen (sha256) != 64)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Invalid rev '%s'", sha256);
-      return FALSE;
-    }
-  return TRUE;
-}
-
-
-void
-ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode)
-{
-  guint32 perms = (mode & ~S_IFMT);
-  g_checksum_update (checksum, (guint8*) &uid, 4);
-  g_checksum_update (checksum, (guint8*) &gid, 4);
-  g_checksum_update (checksum, (guint8*) &perms, 4);
-}
-
-static char *
-canonicalize_xattrs (char *xattr_string, size_t len)
-{
-  char *p;
-  GSList *xattrs = NULL;
-  GSList *iter;
-  GString *result;
-
-  result = g_string_new (0);
-
-  p = xattr_string;
-  while (p < xattr_string+len)
-    {
-      xattrs = g_slist_prepend (xattrs, p);
-      p += strlen (p) + 1;
-    }
-
-  xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
-  for (iter = xattrs; iter; iter = iter->next)
-    g_string_append (result, iter->data);
-
-  g_slist_free (xattrs);
-  return g_string_free (result, FALSE);
-}
-
-static gboolean
-read_xattr_name_array (const char *path,
-                       const char *xattrs,
-                       size_t      len,
-                       GVariantBuilder *builder,
-                       GError  **error)
-{
-  gboolean ret = FALSE;
-  const char *p;
-
-  p = xattrs;
-  while (p < xattrs+len)
-    {
-      ssize_t bytes_read;
-      char *buf;
-
-      bytes_read = lgetxattr (path, p, NULL, 0);
-      if (bytes_read < 0)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-      if (bytes_read == 0)
-        continue;
-
-      buf = g_malloc (bytes_read);
-      if (lgetxattr (path, p, buf, bytes_read) < 0)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          g_free (buf);
-          goto out;
-        }
-      
-      g_variant_builder_add (builder, "(@ay@ay)",
-                             g_variant_new_bytestring (p),
-                             g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
-                                                      buf, bytes_read, FALSE, g_free, buf));
-
-      p = p + strlen (p) + 1;
-    }
-  
-  ret = TRUE;
- out:
-  return ret;
-}
-
-GVariant *
-ostree_get_xattrs_for_path (const char *path,
-                              GError    **error)
-{
-  GVariant *ret = NULL;
-  GVariantBuilder builder;
-  char *xattr_names = NULL;
-  char *xattr_names_canonical = NULL;
-  ssize_t bytes_read;
-
-  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
-
-  bytes_read = llistxattr (path, NULL, 0);
-
-  if (bytes_read < 0)
-    {
-      if (errno != ENOTSUP)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-    }
-  else if (bytes_read > 0)
-    {
-      xattr_names = g_malloc (bytes_read);
-      if (llistxattr (path, xattr_names, bytes_read) < 0)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-      xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
-      
-      if (!read_xattr_name_array (path, xattr_names_canonical, bytes_read, &builder, error))
-        goto out;
-    }
-
-  ret = g_variant_builder_end (&builder);
-  g_variant_ref_sink (ret);
- out:
-  if (!ret)
-    g_variant_builder_clear (&builder);
-  g_free (xattr_names);
-  g_free (xattr_names_canonical);
-  return ret;
-}
-
-gboolean
-ostree_stat_and_checksum_file (int dir_fd, const char *path,
-                               OstreeObjectType objtype,
-                               GChecksum **out_checksum,
-                               struct stat *out_stbuf,
-                               GError **error)
-{
-  GChecksum *content_sha256 = NULL;
-  GChecksum *content_and_meta_sha256 = NULL;
-  char *stat_string = NULL;
-  ssize_t bytes_read;
-  GVariant *xattrs = NULL;
-  int fd = -1;
-  DIR *temp_dir = NULL;
-  char *basename = NULL;
-  gboolean ret = FALSE;
-  char *symlink_target = NULL;
-  char *device_id = NULL;
-  struct stat stbuf;
-
-  basename = g_path_get_basename (path);
-
-  if (dir_fd == -1)
-    {
-      char *dirname = g_path_get_dirname (path);
-      temp_dir = opendir (dirname);
-      if (temp_dir == NULL)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          g_free (dirname);
-        }
-      g_free (dirname);
-      dir_fd = dirfd (temp_dir);
-    }
-
-  if (fstatat (dir_fd, basename, &stbuf, AT_SYMLINK_NOFOLLOW) < 0)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-
-  if (S_ISREG(stbuf.st_mode))
-    {
-      fd = ot_util_open_file_read_at (dir_fd, basename, error);
-      if (fd < 0)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-    }
-
-  if (objtype == OSTREE_OBJECT_TYPE_FILE)
-    {
-      xattrs = ostree_get_xattrs_for_path (path, error);
-      if (!xattrs)
-        goto out;
-    }
-
-  content_sha256 = g_checksum_new (G_CHECKSUM_SHA256);
-  if (S_ISREG(stbuf.st_mode))
-    {
-      guint8 buf[8192];
-
-      while ((bytes_read = read (fd, buf, sizeof (buf))) > 0)
-        g_checksum_update (content_sha256, buf, bytes_read);
-      if (bytes_read < 0)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-    }
-  else if (S_ISLNK(stbuf.st_mode))
-    {
-      symlink_target = g_malloc (PATH_MAX);
-
-      g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
-      
-      bytes_read = readlinkat (dir_fd, basename, symlink_target, PATH_MAX);
-      if (bytes_read < 0)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-      g_checksum_update (content_sha256, (guint8*)symlink_target, bytes_read);
-    }
-  else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode))
-    {
-      g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
-      device_id = g_strdup_printf ("%u", (guint)stbuf.st_rdev);
-      g_checksum_update (content_sha256, (guint8*)device_id, strlen (device_id));
-    }
-  else
-    {
-      g_set_error (error, G_IO_ERROR,
-                   G_IO_ERROR_FAILED,
-                   "Unsupported file '%s' (must be regular, symbolic link, or device)",
-                   path);
-      goto out;
-    }
-
-  content_and_meta_sha256 = g_checksum_copy (content_sha256);
-
-  if (objtype == OSTREE_OBJECT_TYPE_FILE)
-    {
-      ostree_checksum_update_stat (content_and_meta_sha256, stbuf.st_uid,
-                                   stbuf.st_gid, stbuf.st_mode);
-      g_checksum_update (content_and_meta_sha256, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
-    }
-
-  *out_stbuf = stbuf;
-  *out_checksum = content_and_meta_sha256;
-  ret = TRUE;
- out:
-  if (fd >= 0)
-    close (fd);
-  if (temp_dir != NULL)
-    closedir (temp_dir);
-  g_free (symlink_target);
-  g_free (basename);
-  g_free (stat_string);
-  if (xattrs)
-    g_variant_unref (xattrs);
-  if (content_sha256)
-    g_checksum_free (content_sha256);
-  return ret;
-}
-
-gboolean
-ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error)
-{
-  gboolean ret = FALSE;
-  int i, n;
-
-  n = g_variant_n_children (xattrs);
-  for (i = 0; i < n; i++)
-    {
-      const guint8* name;
-      GVariant *value;
-      const guint8* value_data;
-      gsize value_len;
-      gboolean loop_err;
-
-      g_variant_get_child (xattrs, i, "(^&ay@ay)",
-                           &name, &value);
-      value_data = g_variant_get_fixed_array (value, &value_len, 1);
-      
-      loop_err = lsetxattr (path, (char*)name, (char*)value_data, value_len, XATTR_REPLACE) < 0;
-      
-      g_variant_unref (value);
-      if (loop_err)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-gboolean
-ostree_parse_metadata_file (const char                  *path,
-                            OstreeSerializedVariantType *out_type,
-                            GVariant                   **out_variant,
-                            GError                     **error)
-{
-  GMappedFile *mfile = NULL;
-  gboolean ret = FALSE;
-  GVariant *ret_variant = NULL;
-  GVariant *container = NULL;
-  guint32 ret_type;
-
-  mfile = g_mapped_file_new (path, FALSE, error);
-  if (mfile == NULL)
-    {
-      goto out;
-    }
-  else
-    {
-      container = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT),
-                                           g_mapped_file_get_contents (mfile),
-                                           g_mapped_file_get_length (mfile),
-                                           FALSE,
-                                           (GDestroyNotify) g_mapped_file_unref,
-                                           mfile);
-      mfile = NULL;
-      g_variant_ref_sink (container);
-      g_variant_get (container, "(uv)",
-                     &ret_type, &ret_variant);
-      ret_type = GUINT32_FROM_BE (ret_type);
-      if (ret_type <= 0 || ret_type > OSTREE_SERIALIZED_VARIANT_LAST)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Corrupted metadata object '%s'; invalid type %d", path, ret_type);
-          goto out;
-        }
-    }
-
-  ret = TRUE;
-  *out_type = ret_type;
-  *out_variant = g_variant_ref_sink (ret_variant);
-  ret_variant = NULL;
- out:
-  if (ret_variant)
-    g_variant_unref (ret_variant);
-  if (container != NULL)
-    g_variant_unref (container);
-  if (mfile != NULL)
-    g_mapped_file_unref (mfile);
-  return ret;
-}
-
-char *
-ostree_get_relative_object_path (const char *checksum,
-                                 OstreeObjectType type,
-                                 gboolean         archive)
-{
-  GString *path;
-  const char *type_string;
-
-  g_assert (strlen (checksum) == 64);
-
-  path = g_string_new ("objects/");
-
-  g_string_append_len (path, checksum, 2);
-  g_string_append_c (path, '/');
-  g_string_append (path, checksum + 2);
-  switch (type)
-    {
-    case OSTREE_OBJECT_TYPE_FILE:
-      if (archive)
-        type_string = ".packfile";
-      else
-        type_string = ".file";
-      break;
-    case OSTREE_OBJECT_TYPE_META:
-      type_string = ".meta";
-      break;
-    default:
-      g_assert_not_reached ();
-    }
-  g_string_append (path, type_string);
-  return g_string_free (path, FALSE);
-}
-
-gboolean
-ostree_pack_object (GOutputStream     *output,
-                    GFile             *file,
-                    OstreeObjectType  objtype,
-                    GCancellable     *cancellable,
-                    GError          **error)
-{
-  gboolean ret = FALSE;
-  char *path = NULL;
-  GFileInfo *finfo = NULL;
-  GFileInputStream *instream = NULL;
-  gboolean pack_builder_initialized = FALSE;
-  GVariantBuilder pack_builder;
-  GVariant *pack_variant = NULL;
-  GVariant *xattrs = NULL;
-  gsize bytes_written;
-
-  path = g_file_get_path (file);
-
-  finfo = g_file_query_info (file, "standard::type,standard::size,standard::is-symlink,standard::symlink-target,unix::*",
-                             G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
-  if (!finfo)
-    goto out;
-
-  if (objtype == OSTREE_OBJECT_TYPE_META)
-    {
-      guint64 object_size_be = GUINT64_TO_BE ((guint64)g_file_info_get_size (finfo));
-      if (!g_output_stream_write_all (output, &object_size_be, 8, &bytes_written, cancellable, error))
-        goto out;
-
-      instream = g_file_read (file, NULL, error);
-      if (!instream)
-        goto out;
-      
-      if (g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error) < 0)
-        goto out;
-    }
-  else
-    {
-      guint32 uid, gid, mode;
-      guint32 device = 0;
-      guint32 metadata_size_be;
-      const char *target = NULL;
-      guint64 object_size;
-
-      uid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_UID);
-      gid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_GID);
-      mode = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_MODE);
-
-      g_variant_builder_init (&pack_builder, G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT));
-      pack_builder_initialized = TRUE;
-      g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (0));
-      g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (uid));
-      g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (gid));
-      g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (mode));
-
-      xattrs = ostree_get_xattrs_for_path (path, error);
-      if (!xattrs)
-        goto out;
-      g_variant_builder_add (&pack_builder, "@a(ayay)", xattrs);
-
-      if (S_ISREG (mode))
-        {
-          object_size = (guint64)g_file_info_get_size (finfo);
-        }
-      else if (S_ISLNK (mode))
-        {
-          target = g_file_info_get_attribute_byte_string (finfo, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
-          object_size = strlen (target);
-        }
-      else if (S_ISBLK (mode) || S_ISCHR (mode))
-        {
-          device = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_DEVICE);
-          object_size = 4;
-        }
-      else
-        g_assert_not_reached ();
-
-      g_variant_builder_add (&pack_builder, "t", GUINT64_TO_BE (object_size));
-      pack_variant = g_variant_builder_end (&pack_builder);
-      pack_builder_initialized = FALSE;
-
-      metadata_size_be = GUINT32_TO_BE (g_variant_get_size (pack_variant));
-
-      if (!g_output_stream_write_all (output, &metadata_size_be, 4,
-                                      &bytes_written, cancellable, error))
-        goto out;
-      g_assert (bytes_written == 4);
-
-      if (!g_output_stream_write_all (output, g_variant_get_data (pack_variant), g_variant_get_size (pack_variant),
-                                      &bytes_written, cancellable, error))
-        goto out;
-
-      if (S_ISREG (mode))
-        {
-          instream = g_file_read (file, NULL, error);
-          if (!instream)
-            goto out;
-          bytes_written = g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error);
-          if (bytes_written < 0)
-            goto out;
-          if (bytes_written != object_size)
-            {
-              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "File size changed unexpectedly");
-              goto out;
-            }
-        }
-      else if (S_ISLNK (mode))
-        {
-          if (!g_output_stream_write_all (output, target, object_size,
-                                          &bytes_written, cancellable, error))
-            goto out;
-        }
-      else if (S_ISBLK (mode) || S_ISCHR (mode))
-        {
-          guint32 device_be = GUINT32_TO_BE (device);
-          g_assert (object_size == 4);
-          if (!g_output_stream_write_all (output, &device_be, object_size,
-                                          &bytes_written, cancellable, error))
-            goto out;
-          g_assert (bytes_written == 4);
-        }
-      else
-        g_assert_not_reached ();
-    }
-  
-  ret = TRUE;
- out:
-  g_free (path);
-  g_clear_object (&finfo);
-  g_clear_object (&instream);
-  if (xattrs)
-    g_variant_unref (xattrs);
-  if (pack_builder_initialized)
-    g_variant_builder_clear (&pack_builder);
-  if (pack_variant)
-    g_variant_unref (pack_variant);
-  return ret;
-}
-
-static gboolean
-splice_and_checksum (GOutputStream  *out,
-                     GInputStream   *in,
-                     GChecksum      *checksum,
-                     GCancellable   *cancellable,
-                     GError        **error)
-{
-  gboolean ret = FALSE;
-  
-  if (checksum != NULL)
-    {
-      gsize bytes_read, bytes_written;
-      char buf[4096];
-      do
-        {
-          if (!g_input_stream_read_all (in, buf, sizeof(buf), &bytes_read, cancellable, error))
-            goto out;
-          if (checksum)
-            g_checksum_update (checksum, (guint8*)buf, bytes_read);
-          if (!g_output_stream_write_all (out, buf, bytes_read, &bytes_written, cancellable, error))
-            goto out;
-        }
-      while (bytes_read > 0);
-    }
-  else
-    {
-      if (g_output_stream_splice (out, in, 0, cancellable, error) < 0)
-        goto out;
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-static gboolean
-unpack_meta (const char   *path,
-             const char   *dest_path,    
-             GChecksum   **out_checksum,
-             GError      **error)
-{
-  gboolean ret = FALSE;
-  GFile *file = NULL;
-  GFile *dest_file = NULL;
-  GFileInputStream *in = NULL;
-  GChecksum *ret_checksum = NULL;
-  GFileOutputStream *out = NULL;
-
-  file = ot_util_new_file_for_path (path);
-  dest_file = ot_util_new_file_for_path (dest_path);
-
-  if (out_checksum)
-    ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
-
-  in = g_file_read (file, NULL, error);
-  if (!in)
-    goto out;
-
-  out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
-  if (!out)
-    goto out;
-
-  if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error))
-    goto out;
-
-  if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
-    goto out;
-
-  ret = TRUE;
-  if (out_checksum)
-    *out_checksum = ret_checksum;
-  ret_checksum = NULL;
- out:
-  if (!ret)
-    (void) unlink (dest_path);
-  if (ret_checksum)
-    g_checksum_free (ret_checksum);
-  g_clear_object (&file);
-  g_clear_object (&dest_file);
-  g_clear_object (&in);
-  return ret;
-}
-
-
-static gboolean
-unpack_file (const char   *path,
-             const char   *dest_path,    
-             GChecksum   **out_checksum,
-             GError      **error)
-{
-  gboolean ret = FALSE;
-  GFile *file = NULL;
-  GFile *dest_file = NULL;
-  char *metadata_buf = NULL;
-  GVariant *metadata = NULL;
-  GVariant *xattrs = NULL;
-  GFileInputStream *in = NULL;
-  GFileOutputStream *out = NULL;
-  GChecksum *ret_checksum = NULL;
-  guint32 metadata_len;
-  guint32 version, uid, gid, mode;
-  guint64 content_len;
-  gsize bytes_read, bytes_written;
-  int temp_fd = -1;
-
-  file = ot_util_new_file_for_path (path);
-
-  in = g_file_read (file, NULL, error);
-  if (!in)
-    goto out;
-      
-  if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
-    goto out;
-  if (bytes_read != 4)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Corrupted packfile; too short while reading metadata length");
-      goto out;
-    }
-      
-  metadata_len = GUINT32_FROM_BE (metadata_len);
-  metadata_buf = g_malloc (metadata_len);
-
-  if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
-    goto out;
-  if (bytes_read != metadata_len)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Corrupted packfile; too short while reading metadata");
-      goto out;
-    }
-
-  metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
-                                      metadata_buf, metadata_len, FALSE, NULL, NULL);
-      
-  g_variant_get (metadata, "(uuuu@a(ayay)t)",
-                 &version, &uid, &gid, &mode,
-                 &xattrs, &content_len);
-  uid = GUINT32_FROM_BE (uid);
-  gid = GUINT32_FROM_BE (gid);
-  mode = GUINT32_FROM_BE (mode);
-  content_len = GUINT64_FROM_BE (content_len);
-
-  dest_file = ot_util_new_file_for_path (dest_path);
-      
-  if (out_checksum)
-    ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
-
-  if (S_ISREG (mode))
-    {
-      out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
-      if (!out)
-        goto out;
-
-      if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error))
-        goto out;
-
-      if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
-        goto out;
-    }
-  else if (S_ISLNK (mode))
-    {
-      char target[PATH_MAX+1];
-
-      if (!g_input_stream_read_all ((GInputStream*)in, target, sizeof(target)-1, &bytes_read, NULL, error))
-        goto out;
-      target[bytes_read] = '\0';
-      if (ret_checksum)
-        g_checksum_update (ret_checksum, (guint8*)target, bytes_read);
-      if (symlink (target, dest_path) < 0)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-    }
-  else if (S_ISCHR (mode) || S_ISBLK (mode))
-    {
-      guint32 dev;
-
-      if (!g_input_stream_read_all ((GInputStream*)in, &dev, 4, &bytes_read, NULL, error))
-        goto out;
-      if (bytes_read != 4)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Corrupted packfile; too short while reading device id");
-          goto out;
-        }
-      dev = GUINT32_FROM_BE (dev);
-      if (ret_checksum)
-        g_checksum_update (ret_checksum, (guint8*)&dev, 4);
-      if (mknod (dest_path, mode, dev) < 0)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-    }
-  else
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Corrupted packfile; invalid mode %u", mode);
-      goto out;
-    }
-
-  if (!S_ISLNK (mode))
-    {
-      if (chmod (dest_path, mode) < 0)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-    }
-
-  if (!ostree_set_xattrs (dest_path, xattrs, error))
-    goto out;
-
-  if (ret_checksum)
-    {
-      ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
-      g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
-    }
-
-  ret = TRUE;
-  if (out_checksum)
-    *out_checksum = ret_checksum;
-  ret_checksum = NULL;
- out:
-  if (!ret)
-    (void) unlink (dest_path);
-  if (ret_checksum)
-    g_checksum_free (ret_checksum);
-  g_free (metadata_buf);
-  g_clear_object (&file);
-  g_clear_object (&dest_file);
-  g_clear_object (&in);
-  g_clear_object (&out);
-  if (metadata)
-   g_variant_unref (metadata);
-  if (xattrs)
-    g_variant_unref (xattrs);
-  return ret;
-}
-
-gboolean
-ostree_unpack_object (const char   *path,
-                      OstreeObjectType  objtype,
-                      const char   *dest_path,    
-                      GChecksum   **out_checksum,
-                      GError      **error)
-{
-  if (objtype == OSTREE_OBJECT_TYPE_META)
-    return unpack_meta (path, dest_path, out_checksum, error);
-  else
-    return unpack_file (path, dest_path, out_checksum, error);
-}
-  
-
-
diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h
deleted file mode 100644 (file)
index c1fb491..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef _OSTREE_CORE
-#define _OSTREE_CORE
-
-#include <otutil.h>
-
-G_BEGIN_DECLS
-
-#define OSTREE_EMPTY_STRING_SHA256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
-
-typedef enum {
-  OSTREE_OBJECT_TYPE_FILE = 1,
-  OSTREE_OBJECT_TYPE_META = 2,
-} OstreeObjectType;
-
-typedef enum {
-  OSTREE_SERIALIZED_TREE_VARIANT = 1,
-  OSTREE_SERIALIZED_COMMIT_VARIANT = 2,
-  OSTREE_SERIALIZED_DIRMETA_VARIANT = 3,
-  OSTREE_SERIALIZED_XATTR_VARIANT = 4
-} OstreeSerializedVariantType;
-#define OSTREE_SERIALIZED_VARIANT_LAST 4
-
-#define OSTREE_SERIALIZED_VARIANT_FORMAT "(uv)"
-
-/*
- * xattr objects:
- * a(ayay) - array of (name, value) pairs, both binary data, though name is a bytestring
- */
-#define OSTREE_XATTR_GVARIANT_FORMAT "a(ayay)"
-
-#define OSTREE_DIR_META_VERSION 0
-/*
- * dirmeta objects:
- * u - Version
- * u - uid
- * u - gid
- * u - mode
- * a(ayay) - xattrs
- */
-#define OSTREE_DIRMETA_GVARIANT_FORMAT "(uuuua(ayay))"
-
-#define OSTREE_TREE_VERSION 0
-/*
- * Tree objects:
- * u - Version
- * a{sv} - Metadata
- * a(ss) - array of (filename, checksum) for files
- * a(sss) - array of (dirname, tree_checksum, meta_checksum) for directories
- */
-#define OSTREE_TREE_GVARIANT_FORMAT "(ua{sv}a(ss)a(sss)"
-
-#define OSTREE_COMMIT_VERSION 0
-/*
- * Commit objects:
- * u - Version
- * a{sv} - Metadata
- * s - parent checksum (empty string for initial)
- * s - subject 
- * s - body
- * t - Timestamp in seconds since the epoch (UTC)
- * s - Root tree contents
- * s - Root tree metadata
- */
-#define OSTREE_COMMIT_GVARIANT_FORMAT "(ua{sv}ssstss)"
-
-gboolean ostree_validate_checksum_string (const char *sha256,
-                                          GError    **error);
-
-char *ostree_get_relative_object_path (const char *checksum,
-                                       OstreeObjectType type,
-                                       gboolean         archive);
-
-GVariant *ostree_get_xattrs_for_path (const char   *path,
-                                      GError     **error);
-
-gboolean ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error);
-
-gboolean ostree_parse_metadata_file (const char                  *path,
-                                     OstreeSerializedVariantType *out_type,
-                                     GVariant                   **out_variant,
-                                     GError                     **error);
-
-gboolean ostree_stat_and_checksum_file (int dirfd, const char *path,
-                                        OstreeObjectType type,
-                                        GChecksum **out_checksum,
-                                        struct stat *out_stbuf,
-                                        GError **error);
-
-/* Packed files:
- *
- * guint32 metadata_length [metadata gvariant] [content]
- *
- * metadata variant:
- * u - Version
- * u - uid
- * u - gid
- * u - mode
- * a(ayay) - xattrs
- * t - content length
- *
- * And then following the end of the variant is the content.  If
- * symlink, then this is the target; if device, then device ID as
- * network byte order uint32.
- */
-#define OSTREE_PACK_FILE_VARIANT_FORMAT "(uuuua(ayay)t)"
-
-gboolean  ostree_pack_object (GOutputStream     *output,
-                              GFile             *path,
-                              OstreeObjectType  objtype,
-                              GCancellable     *cancellable,
-                              GError          **error);
-
-gboolean ostree_unpack_object (const char   *path,
-                               OstreeObjectType  objtype,
-                               const char   *dest_path,    
-                               GChecksum   **out_checksum,
-                               GError      **error);
-
-void ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode);
-
-
-#endif /* _OSTREE_REPO */
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
deleted file mode 100644 (file)
index 1b58d32..0000000
+++ /dev/null
@@ -1,2133 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#define _GNU_SOURCE
-
-#include "config.h"
-
-#include "ostree.h"
-#include "otutil.h"
-
-#include <gio/gunixoutputstream.h>
-#include <gio/gunixinputstream.h>
-
-static gboolean
-link_one_file (OstreeRepo *self, const char *path,
-               OstreeObjectType type,
-               gboolean ignore_exists, gboolean force,
-               GChecksum **out_checksum,
-               GError **error);
-static char *
-get_object_path (OstreeRepo  *self,
-                 const char    *checksum,
-                 OstreeObjectType type);
-
-enum {
-  PROP_0,
-
-  PROP_PATH
-};
-
-G_DEFINE_TYPE (OstreeRepo, ostree_repo, G_TYPE_OBJECT)
-
-#define GET_PRIVATE(o) \
-  (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_REPO, OstreeRepoPrivate))
-
-typedef struct _OstreeRepoPrivate OstreeRepoPrivate;
-
-struct _OstreeRepoPrivate {
-  char *path;
-  GFile *repo_file;
-  GFile *local_heads_dir;
-  GFile *remote_heads_dir;
-  char *objects_path;
-  char *config_path;
-
-  gboolean inited;
-
-  GKeyFile *config;
-  gboolean archive;
-};
-
-static void
-ostree_repo_finalize (GObject *object)
-{
-  OstreeRepo *self = OSTREE_REPO (object);
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-
-  g_free (priv->path);
-  g_clear_object (&priv->repo_file);
-  g_clear_object (&priv->local_heads_dir);
-  g_clear_object (&priv->remote_heads_dir);
-  g_free (priv->objects_path);
-  g_free (priv->config_path);
-  if (priv->config)
-    g_key_file_free (priv->config);
-
-  G_OBJECT_CLASS (ostree_repo_parent_class)->finalize (object);
-}
-
-static void
-ostree_repo_set_property(GObject         *object,
-                          guint            prop_id,
-                          const GValue    *value,
-                          GParamSpec      *pspec)
-{
-  OstreeRepo *self = OSTREE_REPO (object);
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-
-  switch (prop_id)
-    {
-    case PROP_PATH:
-      priv->path = g_value_dup_string (value);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static void
-ostree_repo_get_property(GObject         *object,
-                          guint            prop_id,
-                          GValue          *value,
-                          GParamSpec      *pspec)
-{
-  OstreeRepo *self = OSTREE_REPO (object);
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-
-  switch (prop_id)
-    {
-    case PROP_PATH:
-      g_value_set_string (value, priv->path);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static GObject *
-ostree_repo_constructor (GType                  gtype,
-                           guint                  n_properties,
-                           GObjectConstructParam *properties)
-{
-  GObject *object;
-  GObjectClass *parent_class;
-  OstreeRepoPrivate *priv;
-
-  parent_class = G_OBJECT_CLASS (ostree_repo_parent_class);
-  object = parent_class->constructor (gtype, n_properties, properties);
-
-  priv = GET_PRIVATE (object);
-
-  g_assert (priv->path != NULL);
-  
-  priv->repo_file = ot_util_new_file_for_path (priv->path);
-  priv->local_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/heads");
-  priv->remote_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/remotes");
-  
-  priv->objects_path = g_build_filename (priv->path, "objects", NULL);
-  priv->config_path = g_build_filename (priv->path, "config", NULL);
-
-  return object;
-}
-
-static void
-ostree_repo_class_init (OstreeRepoClass *klass)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  g_type_class_add_private (klass, sizeof (OstreeRepoPrivate));
-
-  object_class->constructor = ostree_repo_constructor;
-  object_class->get_property = ostree_repo_get_property;
-  object_class->set_property = ostree_repo_set_property;
-  object_class->finalize = ostree_repo_finalize;
-
-  g_object_class_install_property (object_class,
-                                   PROP_PATH,
-                                   g_param_spec_string ("path",
-                                                        "",
-                                                        "",
-                                                        NULL,
-                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-}
-
-static void
-ostree_repo_init (OstreeRepo *self)
-{
-}
-
-OstreeRepo*
-ostree_repo_new (const char *path)
-{
-  return g_object_new (OSTREE_TYPE_REPO, "path", path, NULL);
-}
-
-static gboolean
-parse_rev_file (OstreeRepo     *self,
-                const char     *path,
-                char          **sha256,
-                GError        **error) G_GNUC_UNUSED;
-
-static gboolean
-parse_rev_file (OstreeRepo     *self,
-                const char     *path,
-                char          **sha256,
-                GError        **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  GError *temp_error = NULL;
-  gboolean ret = FALSE;
-  char *rev = NULL;
-
-  rev = ot_util_get_file_contents_utf8 (path, &temp_error);
-  if (rev == NULL)
-    {
-      if (g_error_matches (temp_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
-        {
-          g_clear_error (&temp_error);
-        }
-      else
-        {
-          g_propagate_error (error, temp_error);
-          goto out;
-        }
-    }
-  else
-    {
-      g_strchomp (rev);
-    }
-
-  if (g_str_has_prefix (rev, "ref: "))
-    {
-      GFile *ref;
-      char *ref_path;
-      char *ref_sha256;
-      gboolean subret;
-
-      ref = g_file_resolve_relative_path (priv->local_heads_dir, rev + 5);
-      ref_path = g_file_get_path (ref);
-
-      subret = parse_rev_file (self, ref_path, &ref_sha256, error);
-      g_clear_object (&ref);
-      g_free (ref_path);
-        
-      if (!subret)
-        {
-          g_free (ref_sha256);
-          goto out;
-        }
-      
-      g_free (rev);
-      rev = ref_sha256;
-    }
-  else 
-    {
-      if (!ostree_validate_checksum_string (rev, error))
-        goto out;
-    }
-
-  *sha256 = rev;
-  rev = NULL;
-  ret = TRUE;
- out:
-  g_free (rev);
-  return ret;
-}
-
-static gboolean
-resolve_rev (OstreeRepo     *self,
-             const char     *rev,
-             gboolean        allow_noent,
-             char          **sha256,
-             GError        **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  gboolean ret = FALSE;
-  char *tmp = NULL;
-  char *tmp2 = NULL;
-  char *ret_rev = NULL;
-  GFile *child = NULL;
-  char *child_path = NULL;
-  GError *temp_error = NULL;
-  GVariant *commit = NULL;
-
-  if (strlen (rev) == 0)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Invalid empty rev");
-      goto out;
-    }
-  else if (strlen (rev) == 64)
-    {
-      ret_rev = g_strdup (rev);
-    }
-  else if (g_str_has_suffix (rev, "^"))
-    {
-      tmp = g_strdup (rev);
-      tmp[strlen(tmp) - 1] = '\0';
-
-      if (!resolve_rev (self, tmp, allow_noent, &tmp2, error))
-        goto out;
-
-      if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_COMMIT_VARIANT, tmp2, &commit, error))
-        goto out;
-      
-      g_variant_get_child (commit, 2, "s", &ret_rev);
-      if (strlen (ret_rev) == 0)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Commit %s has no parent", tmp2);
-          goto out;
-
-        }
-    }
-  else
-    {
-      child = g_file_get_child (priv->local_heads_dir, rev);
-      child_path = g_file_get_path (child);
-      if (!ot_util_gfile_load_contents_utf8 (child, NULL, &ret_rev, NULL, &temp_error))
-        {
-          if (allow_noent && g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
-            {
-              g_free (ret_rev);
-              ret_rev = NULL;
-            }
-          else
-            {
-              g_propagate_error (error, temp_error);
-              g_prefix_error (error, "Couldn't open ref '%s': ", child_path);
-              goto out;
-            }
-        }
-      else
-        {
-          g_strchomp (ret_rev);
-         
-          if (!ostree_validate_checksum_string (ret_rev, error))
-            goto out;
-        }
-    }
-
-  *sha256 = ret_rev;
-  ret_rev = NULL;
-  ret = TRUE;
- out:
-  if (commit)
-    g_variant_unref (commit);
-  g_free (tmp);
-  g_free (tmp2);
-  g_clear_object (&child);
-  g_free (child_path);
-  g_free (ret_rev);
-  return ret;
-}
-
-gboolean
-ostree_repo_resolve_rev (OstreeRepo     *self,
-                         const char     *rev,
-                         char          **sha256,
-                         GError        **error)
-{
-  return resolve_rev (self, rev, FALSE, sha256, error);
-}
-
-static gboolean
-write_checksum_file (GFile *parentdir,
-                     const char *name,
-                     const char *sha256,
-                     GError **error)
-{
-  gboolean ret = FALSE;
-  GFile *child = NULL;
-  GOutputStream *out = NULL;
-  gsize bytes_written;
-
-  child = g_file_get_child (parentdir, name);
-
-  if ((out = (GOutputStream*)g_file_replace (child, NULL, FALSE, 0, NULL, error)) == NULL)
-    goto out;
-  if (!g_output_stream_write_all (out, sha256, strlen (sha256), &bytes_written, NULL, error))
-    goto out;
-  if (!g_output_stream_write_all (out, "\n", 1, &bytes_written, NULL, error))
-    goto out;
-  if (!g_output_stream_close (out, NULL, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  g_clear_object (&child);
-  g_clear_object (&out);
-  return ret;
-}
-
-/**
- * ostree_repo_get_config:
- * @self:
- *
- * Returns: (transfer none): The repository configuration; do not modify
- */
-GKeyFile *
-ostree_repo_get_config (OstreeRepo *self)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-
-  g_return_val_if_fail (priv->inited, NULL);
-
-  return priv->config;
-}
-
-/**
- * ostree_repo_copy_config:
- * @self:
- *
- * Returns: (transfer full): A newly-allocated copy of the repository config
- */
-GKeyFile *
-ostree_repo_copy_config (OstreeRepo *self)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  GKeyFile *copy;
-  char *data;
-  gsize len;
-
-  g_return_val_if_fail (priv->inited, NULL);
-
-  copy = g_key_file_new ();
-  data = g_key_file_to_data (priv->config, &len, NULL);
-  if (!g_key_file_load_from_data (copy, data, len, 0, NULL))
-    g_assert_not_reached ();
-  g_free (data);
-  return copy;
-}
-
-/**
- * ostree_repo_write_config:
- * @self:
- * @new_config: Overwrite the config file with this data.  Do not change later!
- * @error: a #GError
- *
- * Save @new_config in place of this repository's config file.  Note
- * that @new_config should not be modified after - this function
- * simply adds a reference.
- */
-gboolean
-ostree_repo_write_config (OstreeRepo *self,
-                          GKeyFile   *new_config,
-                          GError    **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  char *data = NULL;
-  gsize len;
-  gboolean ret = FALSE;
-
-  g_return_val_if_fail (priv->inited, FALSE);
-
-  data = g_key_file_to_data (new_config, &len, error);
-  if (!g_file_set_contents (priv->config_path, data, len, error))
-    goto out;
-  
-  g_key_file_unref (priv->config);
-  priv->config = g_key_file_ref (new_config);
-
-  ret = TRUE;
- out:
-  g_free (data);
-  return ret;
-}
-
-gboolean
-ostree_repo_check (OstreeRepo *self, GError **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  gboolean ret = FALSE;
-  char *version = NULL;;
-  GError *temp_error = NULL;
-
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
-  if (priv->inited)
-    return TRUE;
-
-  if (!g_file_test (priv->objects_path, G_FILE_TEST_IS_DIR))
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Couldn't find objects directory '%s'", priv->objects_path);
-      goto out;
-    }
-  
-  priv->config = g_key_file_new ();
-  if (!g_key_file_load_from_file (priv->config, priv->config_path, 0, error))
-    {
-      g_prefix_error (error, "Couldn't parse config file: ");
-      goto out;
-    }
-
-  version = g_key_file_get_value (priv->config, "core", "repo_version", &temp_error);
-  if (temp_error)
-    {
-      g_propagate_error (error, temp_error);
-      goto out;
-    }
-
-  if (strcmp (version, "0") != 0)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Invalid repository version '%s'", version);
-      goto out;
-    }
-
-  priv->archive = g_key_file_get_boolean (priv->config, "core", "archive", &temp_error);
-  if (temp_error)
-    {
-      if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND))
-        {
-          g_clear_error (&temp_error);
-        }
-      else
-        {
-          g_propagate_error (error, temp_error);
-          goto out;
-        }
-    }
-
-  priv->inited = TRUE;
-  
-  ret = TRUE;
- out:
-  g_free (version);
-  return ret;
-}
-
-const char *
-ostree_repo_get_path (OstreeRepo  *self)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  return priv->path;
-}
-
-gboolean      
-ostree_repo_is_archive (OstreeRepo  *self)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-
-  g_return_val_if_fail (priv->inited, FALSE);
-
-  return priv->archive;
-}
-
-static gboolean
-import_gvariant_object (OstreeRepo  *self,
-                        OstreeSerializedVariantType type,
-                        GVariant       *variant,
-                        GChecksum    **out_checksum,
-                        GError       **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  GVariant *serialized = NULL;
-  gboolean ret = FALSE;
-  gsize bytes_written;
-  char *tmp_name = NULL;
-  int fd = -1;
-  GUnixOutputStream *stream = NULL;
-
-  serialized = g_variant_new ("(uv)", GUINT32_TO_BE ((guint32)type), variant);
-
-  tmp_name = g_build_filename (priv->objects_path, "variant-tmp-XXXXXX", NULL);
-  fd = g_mkstemp (tmp_name);
-  if (fd < 0)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-
-  stream = (GUnixOutputStream*)g_unix_output_stream_new (fd, FALSE);
-  if (!g_output_stream_write_all ((GOutputStream*)stream,
-                                  g_variant_get_data (serialized),
-                                  g_variant_get_size (serialized),
-                                  &bytes_written,
-                                  NULL,
-                                  error))
-    goto out;
-  if (!g_output_stream_close ((GOutputStream*)stream,
-                              NULL, error))
-    goto out;
-
-  if (!link_one_file (self, tmp_name, OSTREE_OBJECT_TYPE_META, 
-                      TRUE, FALSE, out_checksum, error))
-    goto out;
-  
-  ret = TRUE;
- out:
-  /* Unconditionally unlink; if we suceeded, there's a new link, if not, clean up. */
-  (void) unlink (tmp_name);
-  if (fd != -1)
-    close (fd);
-  if (serialized != NULL)
-    g_variant_unref (serialized);
-  g_free (tmp_name);
-  g_clear_object (&stream);
-  return ret;
-}
-
-gboolean
-ostree_repo_load_variant_checked (OstreeRepo  *self,
-                                  OstreeSerializedVariantType expected_type,
-                                  const char    *sha256, 
-                                  GVariant     **out_variant,
-                                  GError       **error)
-{
-  gboolean ret = FALSE;
-  OstreeSerializedVariantType type;
-  GVariant *ret_variant = NULL;
-
-  if (!ostree_repo_load_variant (self, sha256, &type, &ret_variant, error))
-    goto out;
-
-  if (type != expected_type)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Corrupted metadata object '%s'; found type %u, expected %u", sha256,
-                   type, (guint32)expected_type);
-      goto out;
-    }
-
-  ret = TRUE;
-  *out_variant = ret_variant;
-  ret_variant = NULL;
- out:
-  if (ret_variant)
-    g_variant_unref (ret_variant);
-  return ret;
-}
-
-static gboolean
-import_directory_meta (OstreeRepo  *self,
-                       const char *path,
-                       GVariant  **out_variant,
-                       GChecksum **out_checksum,
-                       GError    **error)
-{
-  gboolean ret = FALSE;
-  struct stat stbuf;
-  GChecksum *ret_checksum = NULL;
-  GVariant *dirmeta = NULL;
-  GVariant *xattrs = NULL;
-
-  if (lstat (path, &stbuf) < 0)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-  
-  if (!S_ISDIR(stbuf.st_mode))
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Not a directory: '%s'", path);
-      goto out;
-    }
-
-  xattrs = ostree_get_xattrs_for_path (path, error);
-  if (!xattrs)
-    goto out;
-
-  dirmeta = g_variant_new ("(uuuu@a(ayay))",
-                           OSTREE_DIR_META_VERSION,
-                           GUINT32_TO_BE ((guint32)stbuf.st_uid),
-                           GUINT32_TO_BE ((guint32)stbuf.st_gid),
-                           GUINT32_TO_BE ((guint32)stbuf.st_mode),
-                           xattrs);
-  g_variant_ref_sink (dirmeta);
-
-  if (!import_gvariant_object (self, OSTREE_SERIALIZED_DIRMETA_VARIANT, 
-                               dirmeta, &ret_checksum, error))
-        goto out;
-
-  ret = TRUE;
- out:
-  if (!ret)
-    {
-      if (ret_checksum)
-        g_checksum_free (ret_checksum);
-      if (dirmeta != NULL)
-        g_variant_unref (dirmeta);
-    }
-  else
-    {
-      *out_checksum = ret_checksum;
-      *out_variant = dirmeta;
-    }
-  if (xattrs)
-    g_variant_unref (xattrs);
-  return ret;
-}
-
-static char *
-get_object_path (OstreeRepo  *self,
-                 const char    *checksum,
-                 OstreeObjectType type)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  char *ret;
-  char *relpath;
-
-  relpath = ostree_get_relative_object_path (checksum, type, priv->archive);
-  ret = g_build_filename (priv->path, relpath, NULL);
-  g_free (relpath);
-  return ret;
-}
-
-static char *
-prepare_dir_for_checksum_get_object_path (OstreeRepo *self,
-                                          const char   *checksum,
-                                          OstreeObjectType type,
-                                          GError      **error)
-{
-  char *checksum_dir = NULL;
-  char *object_path = NULL;
-
-  object_path = get_object_path (self, checksum, type);
-  checksum_dir = g_path_get_dirname (object_path);
-
-  if (!ot_util_ensure_directory (checksum_dir, FALSE, error))
-    goto out;
-  
- out:
-  g_free (checksum_dir);
-  return object_path;
-}
-
-static gboolean
-link_object_trusted (OstreeRepo   *self,
-                     const char   *path,
-                     const char   *checksum,
-                     OstreeObjectType objtype,
-                     gboolean      ignore_exists,
-                     gboolean      force,
-                     gboolean     *did_exist,
-                     GError      **error)
-{
-  char *src_basename = NULL;
-  char *src_dirname = NULL;
-  char *dest_basename = NULL;
-  char *tmp_dest_basename = NULL;
-  char *dest_dirname = NULL;
-  DIR *src_dir = NULL;
-  DIR *dest_dir = NULL;
-  gboolean ret = FALSE;
-  char *dest_path = NULL;
-
-  src_basename = g_path_get_basename (path);
-  src_dirname = g_path_get_dirname (path);
-
-  src_dir = opendir (src_dirname);
-  if (src_dir == NULL)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-
-  dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
-  if (!dest_path)
-    goto out;
-
-  dest_basename = g_path_get_basename (dest_path);
-  dest_dirname = g_path_get_dirname (dest_path);
-  dest_dir = opendir (dest_dirname);
-  if (dest_dir == NULL)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-
-  if (force)
-    {
-      tmp_dest_basename = g_strconcat (dest_basename, ".tmp", NULL);
-      (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
-    }
-  else
-    tmp_dest_basename = g_strdup (dest_basename);
-  
-  if (linkat (dirfd (src_dir), src_basename, dirfd (dest_dir), tmp_dest_basename, 0) < 0)
-    {
-      if (errno != EEXIST || !ignore_exists)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-      else
-        *did_exist = TRUE;
-    }
-  else
-    *did_exist = FALSE;
-
-  if (force)
-    {
-      if (renameat (dirfd (dest_dir), tmp_dest_basename, 
-                    dirfd (dest_dir), dest_basename) < 0)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-      (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
-    }
-
-  ret = TRUE;
- out:
-  if (src_dir != NULL)
-    closedir (src_dir);
-  if (dest_dir != NULL)
-    closedir (dest_dir);
-  g_free (src_basename);
-  g_free (src_dirname);
-  g_free (dest_basename);
-  g_free (tmp_dest_basename);
-  g_free (dest_dirname);
-  g_free (dest_path);
-  return ret;
-}
-
-static gboolean
-archive_file_trusted (OstreeRepo   *self,
-                      const char   *path,
-                      const char   *checksum,
-                      OstreeObjectType objtype,
-                      gboolean      ignore_exists,
-                      gboolean      force,
-                      gboolean     *did_exist,
-                      GError      **error)
-{
-  GFile *infile = NULL;
-  GFile *outfile = NULL;
-  GFileOutputStream *out = NULL;
-  gboolean ret = FALSE;
-  char *dest_path = NULL;
-  char *dest_tmp_path = NULL;
-
-  infile = ot_util_new_file_for_path (path);
-
-  dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
-  if (!dest_path)
-    goto out;
-
-  dest_tmp_path = g_strconcat (dest_path, ".tmp", NULL);
-
-  outfile = ot_util_new_file_for_path (dest_tmp_path);
-  out = g_file_replace (outfile, NULL, FALSE, 0, NULL, error);
-  if (!out)
-    goto out;
-
-  if (!ostree_pack_object ((GOutputStream*)out, infile, objtype, NULL, error))
-    goto out;
-  
-  if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
-    goto out;
-
-  if (rename (dest_tmp_path, dest_path) < 0)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-
-  ret = TRUE;
- out:
-  g_free (dest_path);
-  g_free (dest_tmp_path);
-  g_clear_object (&infile);
-  g_clear_object (&outfile);
-  g_clear_object (&out);
-  return ret;
-}
-  
-gboolean      
-ostree_repo_store_object_trusted (OstreeRepo   *self,
-                                  const char   *path,
-                                  const char   *checksum,
-                                  OstreeObjectType objtype,
-                                  gboolean      ignore_exists,
-                                  gboolean      force,
-                                  gboolean     *did_exist,
-                                  GError      **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  if (priv->archive && objtype == OSTREE_OBJECT_TYPE_FILE)
-    return archive_file_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
-  else
-    return link_object_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
-}
-
-static gboolean
-link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
-               gboolean ignore_exists, gboolean force,
-               GChecksum **out_checksum,
-               GError **error)
-{
-  gboolean ret = FALSE;
-  struct stat stbuf;
-  GChecksum *id = NULL;
-  gboolean did_exist;
-
-  if (!ostree_stat_and_checksum_file (-1, path, type, &id, &stbuf, error))
-    goto out;
-
-  if (!ostree_repo_store_object_trusted (self, path, g_checksum_get_string (id), type,
-                                         ignore_exists, force, &did_exist, error))
-    goto out;
-
-  *out_checksum = id;
-  id = NULL;
-  ret = TRUE;
- out:
-  if (id != NULL)
-    g_checksum_free (id);
-  return ret;
-}
-
-gboolean
-ostree_repo_link_file (OstreeRepo *self,
-                       const char   *path,
-                       gboolean      ignore_exists,
-                       gboolean      force,
-                       GError      **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  GChecksum *checksum = NULL;
-
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-  g_return_val_if_fail (priv->inited, FALSE);
-
-  if (!link_one_file (self, path, OSTREE_OBJECT_TYPE_FILE,
-                      ignore_exists, force, &checksum, error))
-    return FALSE;
-  g_checksum_free (checksum);
-  return TRUE;
-}
-
-gboolean
-ostree_repo_store_packfile (OstreeRepo       *self,
-                            const char       *expected_checksum,
-                            const char       *path,
-                            OstreeObjectType  objtype,
-                            GError          **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  gboolean ret = FALSE;
-  GString *tempfile_path = NULL;
-  GChecksum *checksum = NULL;
-  struct stat stbuf;
-  gboolean did_exist;
-
-  tempfile_path = g_string_new (priv->path);
-  g_string_append_printf (tempfile_path, "/tmp-unpack-%s", expected_checksum);
-  
-  if (!ostree_unpack_object (path, objtype, tempfile_path->str, &checksum, error))
-    goto out;
-
-  if (strcmp (g_checksum_get_string (checksum), expected_checksum) != 0)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Corrupted object %s (actual checksum is %s)",
-                   expected_checksum, g_checksum_get_string (checksum));
-      goto out;
-    }
-
-  if (!ostree_repo_store_object_trusted (self, tempfile_path ? tempfile_path->str : path,
-                                         expected_checksum,
-                                         objtype,
-                                         TRUE, FALSE, &did_exist, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  if (tempfile_path)
-    {
-      (void) unlink (tempfile_path->str);
-      g_string_free (tempfile_path, TRUE);
-    }
-  if (checksum)
-    g_checksum_free (checksum);
-  return ret;
-}
-
-typedef struct _ParsedTreeData ParsedTreeData;
-typedef struct _ParsedDirectoryData ParsedDirectoryData;
-
-static void parsed_tree_data_free (ParsedTreeData *pdata);
-
-struct _ParsedDirectoryData {
-  ParsedTreeData *tree_data;
-  char *metadata_sha256;
-  GVariant *meta_data;
-};
-
-static void
-parsed_directory_data_free (ParsedDirectoryData *pdata)
-{
-  if (pdata == NULL)
-    return;
-  parsed_tree_data_free (pdata->tree_data);
-  g_free (pdata->metadata_sha256);
-  g_variant_unref (pdata->meta_data);
-  g_free (pdata);
-}
-
-struct _ParsedTreeData {
-  GHashTable *files;  /* char* filename -> char* checksum */
-  GHashTable *directories;  /* char* dirname -> ParsedDirectoryData* */
-};
-
-static ParsedTreeData *
-parsed_tree_data_new (void)
-{
-  ParsedTreeData *ret = g_new0 (ParsedTreeData, 1);
-  ret->files = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                      (GDestroyNotify)g_free, 
-                                      (GDestroyNotify)g_free);
-  ret->directories = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                            (GDestroyNotify)g_free, 
-                                            (GDestroyNotify)parsed_directory_data_free);
-  return ret;
-}
-
-static void
-parsed_tree_data_free (ParsedTreeData *pdata)
-{
-  if (pdata == NULL)
-    return;
-  g_hash_table_destroy (pdata->files);
-  g_hash_table_destroy (pdata->directories);
-  g_free (pdata);
-}
-
-static gboolean
-parse_tree (OstreeRepo    *self,
-            const char      *sha256,
-            ParsedTreeData **out_pdata,
-            GError         **error)
-{
-  gboolean ret = FALSE;
-  ParsedTreeData *ret_pdata = NULL;
-  int i, n;
-  guint32 version;
-  GVariant *tree_variant = NULL;
-  GVariant *meta_variant = NULL;
-  GVariant *files_variant = NULL;
-  GVariant *dirs_variant = NULL;
-
-  if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_TREE_VARIANT,
-                                         sha256, &tree_variant, error))
-    goto out;
-
-  /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
-  g_variant_get (tree_variant, "(u@a{sv}@a(ss)@a(sss))",
-                 &version, &meta_variant, &files_variant, &dirs_variant);
-  version = GUINT32_FROM_BE (version);
-
-  ret_pdata = parsed_tree_data_new ();
-  n = g_variant_n_children (files_variant);
-  for (i = 0; i < n; i++)
-    {
-      const char *filename;
-      const char *checksum;
-
-      g_variant_get_child (files_variant, i, "(&s&s)", &filename, &checksum);
-
-      g_hash_table_insert (ret_pdata->files, g_strdup (filename), g_strdup (checksum));
-    }
-
-  n = g_variant_n_children (dirs_variant);
-  for (i = 0; i < n; i++)
-    {
-      const char *dirname;
-      const char *tree_checksum;
-      const char *meta_checksum;
-      ParsedTreeData *child_tree = NULL;
-      GVariant *metadata = NULL;
-      ParsedDirectoryData *child_dir = NULL;
-
-      g_variant_get_child (dirs_variant, i, "(&s&s&s)",
-                           &dirname, &tree_checksum, &meta_checksum);
-      
-      if (!parse_tree (self, tree_checksum, &child_tree, error))
-        goto out;
-
-      if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
-                                             meta_checksum, &metadata, error))
-        {
-          parsed_tree_data_free (child_tree);
-          goto out;
-        }
-
-      child_dir = g_new0 (ParsedDirectoryData, 1);
-      child_dir->tree_data = child_tree;
-      child_dir->metadata_sha256 = g_strdup (meta_checksum);
-      child_dir->meta_data = metadata;
-
-      g_hash_table_insert (ret_pdata->directories, g_strdup (dirname), child_dir);
-    }
-
-  ret = TRUE;
- out:
-  if (!ret)
-    parsed_tree_data_free (ret_pdata);
-  else
-    *out_pdata = ret_pdata;
-  if (tree_variant)
-    g_variant_unref (tree_variant);
-  if (meta_variant)
-    g_variant_unref (meta_variant);
-  if (files_variant)
-    g_variant_unref (files_variant);
-  if (dirs_variant)
-    g_variant_unref (dirs_variant);
-  return ret;
-}
-
-static gboolean
-load_commit_and_trees (OstreeRepo   *self,
-                       const char     *commit_sha256,
-                       GVariant      **out_commit,
-                       ParsedDirectoryData **out_root_data,
-                       GError        **error)
-{
-  GVariant *ret_commit = NULL;
-  ParsedDirectoryData *ret_root_data = NULL;
-  ParsedTreeData *tree_data = NULL;
-  char *ret_metadata_checksum = NULL;
-  GVariant *root_metadata = NULL;
-  gboolean ret = FALSE;
-  const char *tree_contents_checksum;
-  const char *tree_meta_checksum;
-
-  if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_COMMIT_VARIANT,
-                                         commit_sha256, &ret_commit, error))
-    goto out;
-
-  /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
-  g_variant_get_child (ret_commit, 6, "&s", &tree_contents_checksum);
-  g_variant_get_child (ret_commit, 7, "&s", &tree_meta_checksum);
-
-  if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
-                                         tree_meta_checksum, &root_metadata, error))
-    goto out;
-
-  if (!parse_tree (self, tree_contents_checksum, &tree_data, error))
-    goto out;
-
-  ret_root_data = g_new0 (ParsedDirectoryData, 1);
-  ret_root_data->tree_data = tree_data;
-  ret_root_data->metadata_sha256 = g_strdup (tree_meta_checksum);
-  ret_root_data->meta_data = root_metadata;
-  root_metadata = NULL;
-
-  ret = TRUE;
-  *out_commit = ret_commit;
-  ret_commit = NULL;
-  *out_root_data = ret_root_data;
-  ret_root_data = NULL;
- out:
-  if (ret_commit)
-    g_variant_unref (ret_commit);
-  parsed_directory_data_free (ret_root_data);
-  g_free (ret_metadata_checksum);
-  if (root_metadata)
-    g_variant_unref (root_metadata);
-  return ret;
-}
-
-static GVariant *
-create_empty_gvariant_dict (void)
-{
-  GVariantBuilder builder;
-  g_variant_builder_init (&builder, G_VARIANT_TYPE("a{sv}"));
-  return g_variant_builder_end (&builder);
-}
-
-static gboolean
-import_parsed_tree (OstreeRepo    *self,
-                    ParsedTreeData  *tree,
-                    GChecksum      **out_checksum,
-                    GError         **error)
-{
-  gboolean ret = FALSE;
-  GVariant *serialized_tree = NULL;
-  gboolean builders_initialized = FALSE;
-  GVariantBuilder files_builder;
-  GVariantBuilder dirs_builder;
-  GHashTableIter hash_iter;
-  gpointer key, value;
-
-  g_variant_builder_init (&files_builder, G_VARIANT_TYPE ("a(ss)"));
-  g_variant_builder_init (&dirs_builder, G_VARIANT_TYPE ("a(sss)"));
-  builders_initialized = TRUE;
-
-  g_hash_table_iter_init (&hash_iter, tree->files);
-  while (g_hash_table_iter_next (&hash_iter, &key, &value))
-    {
-      const char *name = key;
-      const char *checksum = value;
-
-      g_variant_builder_add (&files_builder, "(ss)", name, checksum);
-    }
-
-  g_hash_table_iter_init (&hash_iter, tree->directories);
-  while (g_hash_table_iter_next (&hash_iter, &key, &value))
-    {
-      const char *name = key;
-      GChecksum *dir_checksum = NULL;
-      ParsedDirectoryData *dir = value;
-
-      if (!import_parsed_tree (self, dir->tree_data, &dir_checksum, error))
-        goto out;
-
-      g_variant_builder_add (&dirs_builder, "(sss)",
-                             name, g_checksum_get_string (dir_checksum), dir->metadata_sha256);
-      g_checksum_free (dir_checksum);
-    }
-
-  serialized_tree = g_variant_new ("(u@a{sv}@a(ss)@a(sss))",
-                                   GUINT32_TO_BE (0),
-                                   create_empty_gvariant_dict (),
-                                   g_variant_builder_end (&files_builder),
-                                   g_variant_builder_end (&dirs_builder));
-  builders_initialized = FALSE;
-  g_variant_ref_sink (serialized_tree);
-  if (!import_gvariant_object (self, OSTREE_SERIALIZED_TREE_VARIANT, serialized_tree, out_checksum, error))
-    goto out;
-  
-  ret = TRUE;
- out:
-  if (builders_initialized)
-    {
-      g_variant_builder_clear (&files_builder);
-      g_variant_builder_clear (&dirs_builder);
-    }
-  if (serialized_tree)
-    g_variant_unref (serialized_tree);
-  return ret;
-}
-
-static gboolean
-check_path (const char *filename,
-            GError    **error)
-{
-  gboolean ret = FALSE;
-
-  if (!*filename)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Invalid empty filename");
-      goto out;
-    }
-
-  if (strcmp (filename, ".") == 0)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Self-reference '.' in filename '%s' not allowed (yet)", filename);
-      goto out;
-    }
-  
-  if (ot_util_filename_has_dotdot (filename))
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Path uplink '..' in filename '%s' not allowed (yet)", filename);
-      goto out;
-    }
-  
-  if (g_path_is_absolute (filename))
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Absolute filename '%s' not allowed (yet)", filename);
-      goto out;
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-static gboolean
-walk_parsed_tree (OstreeRepo  *self,
-                  const char    *filename,
-                  ParsedTreeData *tree,
-                  int            *out_filename_index, /* out*/
-                  char          **out_component, /* out, must free */
-                  ParsedTreeData **out_tree, /* out, but do not free */
-                  GError        **error)
-{
-  gboolean ret = FALSE;
-  GPtrArray *components = NULL;
-  ParsedTreeData *current_tree = tree;
-  const char *component = NULL;
-  const char *file_sha1 = NULL;
-  ParsedDirectoryData *dir = NULL;
-  int i;
-  int ret_filename_index = 0;
-
-  components = ot_util_path_split (filename);
-  g_assert (components != NULL);
-
-  current_tree = tree;
-  for (i = 0; i < components->len - 1; i++)
-    {
-      component = components->pdata[i];
-      file_sha1 = g_hash_table_lookup (current_tree->files, component);
-      dir = g_hash_table_lookup (current_tree->directories, component);
-
-      if (!(file_sha1 || dir))
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "No such file or directory: %s",
-                       filename);
-          goto out;
-        }
-      else if (file_sha1)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Encountered non-directory '%s' in '%s'",
-                       (char*)component,
-                       filename);
-          goto out;
-        }
-      else
-        {
-          g_assert (dir != NULL);
-          current_tree = dir->tree_data;
-          ret_filename_index++;
-        }
-    }
-
-  ret = TRUE;
-  *out_filename_index = i;
-  *out_component = components->pdata[components->len-1];
-  components->pdata[components->len-1] = NULL; /* steal */
-  *out_tree = current_tree;
- out:
-  g_ptr_array_free (components, TRUE);
-  return ret;
-}
-
-static gboolean
-remove_files_from_tree (OstreeRepo   *self,
-                        const char     *base,
-                        GPtrArray      *removed_files,
-                        ParsedTreeData *tree,
-                        GError        **error)
-{
-  gboolean ret = FALSE;
-  int i;
-
-  for (i = 0; i < removed_files->len; i++)
-    {
-      const char *filename = removed_files->pdata[i];
-      int filename_index;
-      char *component = NULL;
-      ParsedTreeData *parent;
-      const char *file_sha1;
-      ParsedTreeData *dir;
-
-      if (!check_path (filename, error))
-        goto out;
-       
-      if (!walk_parsed_tree (self, filename, tree,
-                             &filename_index, (char**)&component, &parent,
-                             error))
-        goto out;
-
-      file_sha1 = g_hash_table_lookup (parent->files, component);
-      dir = g_hash_table_lookup (parent->directories, component);
-
-      if (file_sha1)
-        g_hash_table_remove (parent->files, component);
-      else if (dir)
-        g_hash_table_remove (parent->directories, component);
-      else
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "No such file or directory: %s",
-                       filename);
-          g_free (component);
-          goto out;
-        }
-      g_free (component);
-    }
-  
-  ret = TRUE;
- out:
-  return ret;
-}
-
-static gboolean
-add_one_directory_to_tree_and_import (OstreeRepo   *self,
-                                      const char     *basename,
-                                      const char     *abspath,
-                                      ParsedTreeData *tree,
-                                      ParsedDirectoryData **dir, /*inout*/
-                                      GError        **error)
-{
-  gboolean ret = FALSE;
-  GVariant *dirmeta = NULL;
-  GChecksum *dir_meta_checksum = NULL;
-  ParsedDirectoryData *dir_value = *dir;
-  
-  g_assert (tree != NULL);
-
-  if (!import_directory_meta (self, abspath, &dirmeta, &dir_meta_checksum, error))
-    goto out;
-
-  if (dir_value)
-    {
-      g_variant_unref (dir_value->meta_data);
-      dir_value->meta_data = dirmeta;
-    }
-  else
-    {
-      dir_value = g_new0 (ParsedDirectoryData, 1);
-      dir_value->tree_data = parsed_tree_data_new ();
-      dir_value->metadata_sha256 = g_strdup (g_checksum_get_string (dir_meta_checksum));
-      dir_value->meta_data = dirmeta;
-      g_hash_table_insert (tree->directories, g_strdup (basename), dir_value);
-    }
-
-  ret = TRUE;
-  *dir = dir_value;
- out:
-  if (dir_meta_checksum)
-    g_checksum_free (dir_meta_checksum);
-  return ret;
-}
-
-static gboolean
-add_one_file_to_tree_and_import (OstreeRepo   *self,
-                                 const char     *basename,
-                                 const char     *abspath,
-                                 ParsedTreeData *tree,
-                                 GError        **error)
-{
-  gboolean ret = FALSE;
-  GChecksum *checksum = NULL;
-  
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-  g_assert (tree != NULL);
-
-  if (!link_one_file (self, abspath, OSTREE_OBJECT_TYPE_FILE,
-                      TRUE, FALSE, &checksum, error))
-    goto out;
-
-  g_hash_table_replace (tree->files, g_strdup (basename),
-                        g_strdup (g_checksum_get_string (checksum)));
-
-  ret = TRUE;
- out:
-  if (checksum)
-    g_checksum_free (checksum);
-  return ret;
-}
-
-static gboolean
-add_one_path_to_tree_and_import (OstreeRepo     *self,
-                                 const char     *base,
-                                 const char     *filename,
-                                 ParsedTreeData *tree,
-                                 GError        **error)
-{
-  gboolean ret = FALSE;
-  GPtrArray *components = NULL;
-  struct stat stbuf;
-  char *component_abspath = NULL;
-  ParsedTreeData *current_tree = tree;
-  const char *component = NULL;
-  const char *file_sha1;
-  char *abspath = NULL;
-  ParsedDirectoryData *dir;
-  int i;
-  gboolean is_directory;
-
-  if (!check_path (filename, error))
-    goto out;
-
-  abspath = g_build_filename (base, filename, NULL);
-
-  if (lstat (abspath, &stbuf) < 0)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-  is_directory = S_ISDIR(stbuf.st_mode);
-       
-  if (components)
-    g_ptr_array_free (components, TRUE);
-  components = ot_util_path_split (filename);
-  g_assert (components->len > 0);
-
-  current_tree = tree;
-  for (i = 0; i < components->len; i++)
-    {
-      component = components->pdata[i];
-      g_free (component_abspath);
-      component_abspath = ot_util_path_join_n (base, components, i);
-      file_sha1 = g_hash_table_lookup (current_tree->files, component);
-      dir = g_hash_table_lookup (current_tree->directories, component);
-
-      g_assert_cmpstr (component, !=, ".");
-
-      if (i < components->len - 1)
-        {
-          if (file_sha1 != NULL)
-            {
-              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "Encountered non-directory '%s' in '%s'",
-                           component,
-                           filename);
-              goto out;
-            }
-          /* Implicitly add intermediate directories */
-          if (!add_one_directory_to_tree_and_import (self, component,
-                                                     component_abspath, current_tree, &dir,
-                                                     error))
-            goto out;
-          g_assert (dir != NULL);
-          current_tree = dir->tree_data;
-        }
-      else if (is_directory)
-        {
-          if (file_sha1 != NULL)
-            {
-              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "File '%s' can't be overwritten by directory",
-                           filename);
-              goto out;
-            }
-          if (!add_one_directory_to_tree_and_import (self, component,
-                                                     abspath, current_tree, &dir,
-                                                     error))
-            goto out;
-        }
-      else 
-        {
-          g_assert (!is_directory);
-          if (dir != NULL)
-            {
-              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "File '%s' can't be overwritten by directory",
-                           filename);
-              goto out;
-            }
-          if (!add_one_file_to_tree_and_import (self, component, abspath,
-                                                current_tree, error))
-            goto out;
-        }
-    }
-
-  ret = TRUE;
- out:
-  if (components)
-    g_ptr_array_unref (components);
-  g_free (component_abspath);
-  g_free (abspath);
-  return ret;
-}
-
-static gboolean
-add_files_to_tree_and_import (OstreeRepo   *self,
-                              const char     *base,
-                              GPtrArray      *added_files,
-                              ParsedTreeData *tree,
-                              GError        **error)
-{
-  gboolean ret = FALSE;
-  int i;
-
-  for (i = 0; i < added_files->len; i++)
-    {
-      const char *path = added_files->pdata[i];
-
-      if (!add_one_path_to_tree_and_import (self, base, path, tree, error))
-        goto out;
-    }
-  
-  ret = TRUE;
- out:
-  return ret;
-}
-
-gboolean      
-ostree_repo_write_ref (OstreeRepo  *self,
-                       gboolean     is_local,
-                       const char  *name,
-                       const char  *rev,
-                       GError     **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  return write_checksum_file (is_local ? priv->local_heads_dir : priv->remote_heads_dir, 
-                              name, rev, error);
-}
-
-static gboolean
-commit_parsed_tree (OstreeRepo *self,
-                    const char   *branch,
-                    const char   *parent,
-                    const char   *subject,
-                    const char   *body,
-                    GVariant     *metadata,
-                    ParsedDirectoryData *root,
-                    GChecksum   **out_commit,
-                    GError      **error)
-{
-  gboolean ret = FALSE;
-  GChecksum *root_checksum = NULL;
-  GChecksum *ret_commit = NULL;
-  GVariant *commit = NULL;
-  GDateTime *now = NULL;
-
-  g_assert (branch != NULL);
-  g_assert (subject != NULL);
-
-  if (!import_parsed_tree (self, root->tree_data, &root_checksum, error))
-    goto out;
-
-  now = g_date_time_new_now_utc ();
-  commit = g_variant_new ("(u@a{sv}ssstss)",
-                          GUINT32_TO_BE (OSTREE_COMMIT_VERSION),
-                          create_empty_gvariant_dict (),
-                          parent ? parent : "",
-                          subject, body ? body : "",
-                          GUINT64_TO_BE (g_date_time_to_unix (now)),
-                          g_checksum_get_string (root_checksum),
-                          root->metadata_sha256);
-  g_variant_ref_sink (commit);
-  if (!import_gvariant_object (self, OSTREE_SERIALIZED_COMMIT_VARIANT,
-                               commit, &ret_commit, error))
-    goto out;
-
-  if (!ostree_repo_write_ref (self, TRUE, branch, g_checksum_get_string (ret_commit), error))
-    goto out;
-
-  ret = TRUE;
-  *out_commit = ret_commit;
- out:
-  if (root_checksum)
-    g_checksum_free (root_checksum);
-  if (commit)
-    g_variant_unref (commit);
-  if (now)
-    g_date_time_unref (now);
-  return ret;
-}
-
-static gboolean
-import_root (OstreeRepo           *self,
-             const char           *base,
-             ParsedDirectoryData **out_root,
-             GError              **error)
-{
-  gboolean ret = FALSE;
-  ParsedDirectoryData *ret_root = NULL;
-  GVariant *root_metadata = NULL;
-  GChecksum *root_meta_checksum = NULL;
-
-  if (!import_directory_meta (self, base, &root_metadata, &root_meta_checksum, error))
-    goto out;
-
-  ret_root = g_new0 (ParsedDirectoryData, 1);
-  ret_root->tree_data = parsed_tree_data_new ();
-  ret_root->meta_data = root_metadata;
-  root_metadata = NULL;
-  ret_root->metadata_sha256 = g_strdup (g_checksum_get_string (root_meta_checksum));
-
-  ret = TRUE;
-  *out_root = ret_root;
-  ret_root = NULL;
- out:
-  if (root_metadata)
-    g_variant_unref (root_metadata);
-  if (root_meta_checksum)
-    g_checksum_free (root_meta_checksum);
-  parsed_directory_data_free (ret_root);
-  return ret;
-}
-
-gboolean
-ostree_repo_commit (OstreeRepo *self,
-                    const char   *branch,
-                    const char   *parent,
-                    const char   *subject,
-                    const char   *body,
-                    GVariant     *metadata,
-                    const char   *base,
-                    GPtrArray    *modified_files,
-                    GPtrArray    *removed_files,
-                    GChecksum   **out_commit,
-                    GError      **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  gboolean ret = FALSE;
-  ParsedDirectoryData *root = NULL;
-  GVariant *previous_commit = NULL;
-  GChecksum *ret_commit_checksum = NULL;
-  GVariant *root_metadata = NULL;
-  GChecksum *root_meta_checksum = NULL;
-  char *current_head = NULL;
-
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-  g_return_val_if_fail (priv->inited, FALSE);
-  g_return_val_if_fail (branch != NULL, FALSE);
-  g_return_val_if_fail (subject != NULL, FALSE);
-
-  if (parent == NULL)
-    parent = branch;
-
-  if (!resolve_rev (self, parent, TRUE, &current_head, error))
-    goto out;
-
-  if (current_head)
-    {
-      if (!load_commit_and_trees (self, current_head, &previous_commit, &root, error))
-        goto out;
-      if (!import_directory_meta (self, base, &root_metadata, &root_meta_checksum, error))
-        goto out;
-      g_variant_unref (root->meta_data);
-      root->meta_data = root_metadata;
-      root_metadata = NULL;
-      root->metadata_sha256 = g_strdup (g_checksum_get_string (root_meta_checksum));
-    }
-  else
-    {
-      /* Initial commit */
-      if (!import_root (self, base, &root, error))
-        goto out;
-    }
-
-  if (!remove_files_from_tree (self, base, removed_files, root->tree_data, error))
-    goto out;
-
-  if (!add_files_to_tree_and_import (self, base, modified_files, root->tree_data, error))
-    goto out;
-
-  if (!commit_parsed_tree (self, branch, current_head,
-                           subject, body, metadata, root,
-                           &ret_commit_checksum, error))
-    goto out;
-  
-  ret = TRUE;
-  *out_commit = ret_commit_checksum;
-  ret_commit_checksum = NULL;
- out:
-  if (ret_commit_checksum)
-    g_checksum_free (ret_commit_checksum);
-  g_free (current_head);
-  if (previous_commit)
-    g_variant_unref (previous_commit);
-  parsed_directory_data_free (root);
-  if (root_metadata)
-    g_variant_unref (root_metadata);
-  if (root_meta_checksum)
-    g_checksum_free (root_meta_checksum);
-  return ret;
-}
-
-gboolean      
-ostree_repo_commit_from_filelist_fd (OstreeRepo *self,
-                                     const char   *branch,
-                                     const char   *parent,
-                                     const char   *subject,
-                                     const char   *body,
-                                     GVariant     *metadata,
-                                     const char   *base,
-                                     int           fd,
-                                     char          separator,
-                                     GChecksum   **out_commit,
-                                     GError      **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  gboolean ret = FALSE;
-  ParsedDirectoryData *root = NULL;
-  GChecksum *ret_commit_checksum = NULL;
-  GUnixInputStream *in = NULL;
-  GDataInputStream *datain = NULL;
-  char *filename = NULL;
-  gsize filename_len;
-  GError *temp_error = NULL;
-  GVariant *root_metadata = NULL;
-  GChecksum *root_meta_checksum = NULL;
-  char *current_head = NULL;
-
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-  g_return_val_if_fail (priv->inited, FALSE);
-  g_return_val_if_fail (branch != NULL, FALSE);
-  g_return_val_if_fail (subject != NULL, FALSE);
-
-  if (parent == NULL)
-    parent = branch;
-
-  /* We're overwriting the tree */
-  if (!import_root (self, base, &root, error))
-    goto out;
-
-  if (!resolve_rev (self, parent, TRUE, &current_head, error))
-    goto out;
-
-  in = (GUnixInputStream*)g_unix_input_stream_new (fd, FALSE);
-  datain = g_data_input_stream_new ((GInputStream*)in);
-
-  while ((filename = g_data_input_stream_read_upto (datain, &separator, 1,
-                                                    &filename_len, NULL, &temp_error)) != NULL)
-    {
-      if (!g_data_input_stream_read_byte (datain, NULL, &temp_error))
-        {
-          if (temp_error != NULL)
-            {
-              g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
-              goto out;
-            }
-        }
-      if (!add_one_path_to_tree_and_import (self, base, filename, root->tree_data, error))
-        goto out;
-      g_free (filename);
-      filename = NULL;
-    }
-  if (filename == NULL && temp_error != NULL)
-    {
-      g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
-      goto out;
-    }
-  if (!commit_parsed_tree (self, branch, current_head, subject, body, metadata,
-                           root, &ret_commit_checksum, error))
-    goto out;
-  
-  ret = TRUE;
-  *out_commit = ret_commit_checksum;
-  ret_commit_checksum = NULL;
- out:
-  if (ret_commit_checksum)
-    g_checksum_free (ret_commit_checksum);
-  g_free (current_head);
-  if (root_metadata)
-    g_variant_unref (root_metadata);
-  if (root_meta_checksum)
-    g_checksum_free (root_meta_checksum);
-  g_clear_object (&datain);
-  g_clear_object (&in);
-  g_free (filename);
-  parsed_directory_data_free (root);
-  return ret;
-  
-}
-
-static gboolean
-iter_object_dir (OstreeRepo   *self,
-                 GFile          *dir,
-                 OstreeRepoObjectIter  callback,
-                 gpointer                user_data,
-                 GError                **error)
-{
-  gboolean ret = FALSE;
-  GError *temp_error = NULL;
-  GFileEnumerator *enumerator = NULL;
-  GFileInfo *file_info = NULL;
-  char *dirpath = NULL;
-
-  dirpath = g_file_get_path (dir);
-
-  enumerator = g_file_enumerate_children (dir, "standard::name,standard::type,unix::*", 
-                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                          NULL, 
-                                          error);
-  if (!enumerator)
-    goto out;
-  
-  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
-    {
-      const char *name;
-      guint32 type;
-      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
-      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
-      
-      if (type != G_FILE_TYPE_DIRECTORY
-          && (g_str_has_suffix (name, ".meta")
-              || g_str_has_suffix (name, ".file")
-              || g_str_has_suffix (name, ".packfile")))
-        {
-          char *dot;
-          char *path;
-          
-          dot = strrchr (name, '.');
-          g_assert (dot);
-          
-          if ((dot - name) == 62)
-            {
-              path = g_build_filename (dirpath, name, NULL);
-              callback (self, path, file_info, user_data);
-              g_free (path);
-            }
-        }
-
-      g_object_unref (file_info);
-    }
-  if (file_info == NULL && temp_error != NULL)
-    {
-      g_propagate_error (error, temp_error);
-      goto out;
-    }
-  if (!g_file_enumerator_close (enumerator, NULL, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  g_free (dirpath);
-  return ret;
-}
-
-gboolean
-ostree_repo_iter_objects (OstreeRepo  *self,
-                            OstreeRepoObjectIter callback,
-                            gpointer       user_data,
-                            GError        **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  GFile *objectdir = NULL;
-  GFileEnumerator *enumerator = NULL;
-  gboolean ret = FALSE;
-  GFileInfo *file_info = NULL;
-  GError *temp_error = NULL;
-
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-  g_return_val_if_fail (priv->inited, FALSE);
-
-  objectdir = ot_util_new_file_for_path (priv->objects_path);
-  enumerator = g_file_enumerate_children (objectdir, "standard::name,standard::type,unix::*", 
-                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                          NULL, 
-                                          error);
-  if (!enumerator)
-    goto out;
-
-  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
-    {
-      const char *name;
-      guint32 type;
-
-      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
-      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
-      
-      if (strlen (name) == 2 && type == G_FILE_TYPE_DIRECTORY)
-        {
-          GFile *objdir = g_file_get_child (objectdir, name);
-          if (!iter_object_dir (self, objdir, callback, user_data, error))
-            {
-              g_object_unref (objdir);
-              goto out;
-            }
-          g_object_unref (objdir);
-        }
-      g_object_unref (file_info);
-    }
-  if (file_info == NULL && temp_error != NULL)
-    {
-      g_propagate_error (error, temp_error);
-      goto out;
-    }
-  if (!g_file_enumerator_close (enumerator, NULL, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  g_clear_object (&file_info);
-  g_clear_object (&enumerator);
-  g_clear_object (&objectdir);
-  return ret;
-}
-
-gboolean
-ostree_repo_load_variant (OstreeRepo *self,
-                          const char   *sha256,
-                          OstreeSerializedVariantType *out_type,
-                          GVariant    **out_variant,
-                          GError      **error)
-{
-  gboolean ret = FALSE;
-  OstreeSerializedVariantType ret_type;
-  GVariant *ret_variant = NULL;
-  char *path = NULL;
-
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
-  path = get_object_path (self, sha256, OSTREE_OBJECT_TYPE_META);
-  if (!ostree_parse_metadata_file (path, &ret_type, &ret_variant, error))
-    goto out;
-
-  ret = TRUE;
-  *out_type = ret_type;
-  *out_variant = ret_variant;
-  ret_variant = NULL;
- out:
-  if (ret_variant)
-    g_variant_unref (ret_variant);
-  g_free (path);
-  return ret;
-}
-
-static gboolean
-checkout_tree (OstreeRepo    *self,
-               ParsedTreeData  *tree,
-               const char      *destination,
-               GError         **error);
-
-static gboolean
-checkout_one_directory (OstreeRepo  *self,
-                        const char *destination,
-                        const char *dirname,
-                        ParsedDirectoryData *dir,
-                        GError         **error)
-{
-  gboolean ret = FALSE;
-  char *dest_path = NULL;
-  guint32 version, uid, gid, mode;
-  GVariant *xattr_variant = NULL;
-
-  dest_path = g_build_filename (destination, dirname, NULL);
-      
-  /* PARSE OSTREE_SERIALIZED_DIRMETA_VARIANT */
-  g_variant_get (dir->meta_data, "(uuuu@a(ayay))",
-                 &version, &uid, &gid, &mode,
-                 &xattr_variant);
-  version = GUINT32_FROM_BE (version);
-  uid = GUINT32_FROM_BE (uid);
-  gid = GUINT32_FROM_BE (gid);
-  mode = GUINT32_FROM_BE (mode);
-
-  if (mkdir (dest_path, (mode_t)mode) < 0)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      g_prefix_error (error, "Failed to create directory '%s': ", dest_path);
-      goto out;
-    }
-      
-  if (!checkout_tree (self, dir->tree_data, dest_path, error))
-    goto out;
-
-  if (!ostree_set_xattrs (dest_path, xattr_variant, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  g_free (dest_path);
-  g_variant_unref (xattr_variant);
-  return ret;
-}
-
-static gboolean
-checkout_tree (OstreeRepo    *self,
-               ParsedTreeData  *tree,
-               const char      *destination,
-               GError         **error)
-{
-  OstreeRepoPrivate *priv = GET_PRIVATE (self);
-  gboolean ret = FALSE;
-  GHashTableIter hash_iter;
-  gpointer key, value;
-
-  g_hash_table_iter_init (&hash_iter, tree->files);
-  while (g_hash_table_iter_next (&hash_iter, &key, &value))
-    {
-      const char *filename = key;
-      const char *checksum = value;
-      char *object_path;
-      char *dest_path;
-
-      object_path = get_object_path (self, checksum, OSTREE_OBJECT_TYPE_FILE);
-      dest_path = g_build_filename (destination, filename, NULL);
-      
-      if (priv->archive)
-        {
-          if (!ostree_unpack_object (object_path, OSTREE_OBJECT_TYPE_FILE, dest_path, NULL, error))
-            goto out;
-        }
-      else
-        {
-          if (link (object_path, dest_path) < 0)
-            {
-              ot_util_set_error_from_errno (error, errno);
-              g_free (object_path);
-              g_free (dest_path);
-              goto out;
-            }
-          g_free (object_path);
-          g_free (dest_path);
-        }
-    }
-
-  g_hash_table_iter_init (&hash_iter, tree->directories);
-  while (g_hash_table_iter_next (&hash_iter, &key, &value))
-    {
-      const char *dirname = key;
-      ParsedDirectoryData *dir = value;
-      
-      if (!checkout_one_directory (self, destination, dirname, dir, error))
-        goto out;
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-gboolean
-ostree_repo_checkout (OstreeRepo *self,
-                      const char   *rev,
-                      const char   *destination,
-                      GError      **error)
-{
-  gboolean ret = FALSE;
-  GVariant *commit = NULL;
-  char *resolved = NULL;
-  char *root_meta_sha = NULL;
-  ParsedDirectoryData *root = NULL;
-
-  if (g_file_test (destination, G_FILE_TEST_EXISTS))
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Destination path '%s' already exists",
-                   destination);
-      goto out;
-    }
-
-  if (!resolve_rev (self, rev, FALSE, &resolved, error))
-    goto out;
-
-  if (!load_commit_and_trees (self, resolved, &commit, &root, error))
-    goto out;
-
-  if (!checkout_one_directory (self, destination, NULL, root, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  g_free (resolved);
-  if (commit)
-    g_variant_unref (commit);
-  parsed_directory_data_free (root);
-  g_free (root_meta_sha);
-  return ret;
-}
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
deleted file mode 100644 (file)
index e9a690a..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-/* ostree-repo.h */
-
-#ifndef _OSTREE_REPO
-#define _OSTREE_REPO
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define OSTREE_TYPE_REPO ostree_repo_get_type()
-#define OSTREE_REPO(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_REPO, OstreeRepo))
-#define OSTREE_REPO_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_REPO, OstreeRepoClass))
-#define OSTREE_IS_REPO(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_REPO))
-#define OSTREE_IS_REPO_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_REPO))
-#define OSTREE_REPO_GET_CLASS(obj) \
-  (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_REPO, OstreeRepoClass))
-
-typedef struct {
-  GObject parent;
-} OstreeRepo;
-
-typedef struct {
-  GObjectClass parent_class;
-} OstreeRepoClass;
-
-GType ostree_repo_get_type (void);
-
-OstreeRepo* ostree_repo_new (const char *path);
-
-gboolean      ostree_repo_check (OstreeRepo  *self, GError **error);
-
-const char *  ostree_repo_get_path (OstreeRepo  *self);
-
-gboolean      ostree_repo_is_archive (OstreeRepo  *self);
-
-GKeyFile *    ostree_repo_get_config (OstreeRepo *self);
-
-GKeyFile *    ostree_repo_copy_config (OstreeRepo *self);
-
-gboolean      ostree_repo_write_config (OstreeRepo *self,
-                                        GKeyFile   *new_config,
-                                        GError    **error);
-
-gboolean      ostree_repo_link_file (OstreeRepo *self,
-                                     const char   *path,
-                                     gboolean      ignore_exists,
-                                     gboolean      force,
-                                     GError      **error);
-
-gboolean      ostree_repo_store_packfile (OstreeRepo       *self,
-                                           const char       *expected_checksum,
-                                           const char       *path,
-                                           OstreeObjectType  objtype,
-                                           GError          **error);
-
-gboolean      ostree_repo_store_object_trusted (OstreeRepo   *self,
-                                                const char   *path,
-                                                const char   *checksum,
-                                                OstreeObjectType objtype,
-                                                gboolean      ignore_exists,
-                                                gboolean      force,
-                                                gboolean     *did_exist,
-                                                GError      **error);
-
-gboolean      ostree_repo_resolve_rev (OstreeRepo  *self,
-                                       const char  *rev,
-                                       char       **out_resolved,
-                                       GError     **error);
-
-gboolean      ostree_repo_write_ref (OstreeRepo  *self,
-                                     gboolean     is_local,
-                                     const char  *name,
-                                     const char  *rev,
-                                     GError     **error);
-
-gboolean      ostree_repo_load_variant (OstreeRepo *self,
-                                          const char   *sha256,
-                                          OstreeSerializedVariantType *out_type,
-                                          GVariant    **out_variant,
-                                          GError      **error);
-
-gboolean      ostree_repo_load_variant_checked (OstreeRepo  *self,
-                                                OstreeSerializedVariantType expected_type,
-                                                const char    *sha256, 
-                                                GVariant     **out_variant,
-                                                GError       **error);
-
-gboolean      ostree_repo_commit (OstreeRepo   *self,
-                                  const char   *branch,
-                                  const char   *parent,
-                                  const char   *subject,
-                                  const char   *body,
-                                  GVariant     *metadata,
-                                  const char   *base,
-                                  GPtrArray    *modified_files,
-                                  GPtrArray    *removed_files,
-                                  GChecksum   **out_commit,
-                                  GError      **error);
-
-gboolean      ostree_repo_commit_from_filelist_fd (OstreeRepo   *self,
-                                                   const char   *branch,
-                                                   const char   *parent,
-                                                   const char   *subject,
-                                                   const char   *body,
-                                                   GVariant     *metadata,
-                                                   const char   *base,
-                                                   int           fd,
-                                                   char          separator,
-                                                   GChecksum   **out_commit,
-                                                   GError      **error);
-
-gboolean      ostree_repo_checkout (OstreeRepo *self,
-                                      const char   *ref,
-                                      const char   *destination,
-                                      GError      **error);
-
-typedef void (*OstreeRepoObjectIter) (OstreeRepo *self, const char *path,
-                                        GFileInfo *fileinfo, gpointer user_data);
-
-gboolean     ostree_repo_iter_objects (OstreeRepo  *self,
-                                         OstreeRepoObjectIter callback,
-                                         gpointer       user_data,
-                                         GError        **error);
-
-G_END_DECLS
-
-#endif /* _OSTREE_REPO */
diff --git a/src/libostree/ostree.h b/src/libostree/ostree.h
deleted file mode 100644 (file)
index f2b23d1..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_H__
-
-#include <ostree-core.h>
-#include <ostree-repo.h>
-#include <ostree-checkout.h>
-
-#endif
diff --git a/src/libotutil/ot-gio-utils.c b/src/libotutil/ot-gio-utils.c
deleted file mode 100644 (file)
index ad172de..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-#include <gio/gunixinputstream.h>
-
-#include <string.h>
-
-#include "otutil.h"
-
-gboolean
-ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error)
-{
-  GFile *dir;
-  GError *temp_error = NULL;
-  gboolean ret = FALSE;
-
-  dir = g_file_new_for_path (path);
-  if (with_parents)
-    ret = g_file_make_directory_with_parents (dir, NULL, &temp_error);
-  else
-    ret = g_file_make_directory (dir, NULL, &temp_error);
-  if (!ret)
-    {
-      if (!g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
-        {
-          g_propagate_error (error, temp_error);
-          goto out;
-        }
-      else
-        g_clear_error (&temp_error);
-    }
-
-  ret = TRUE;
- out:
-  g_clear_object (&dir);
-  return ret;
-}
-
-
-char *
-ot_util_get_file_contents_utf8 (const char *path,
-                                GError    **error)
-{
-  GFile *file = NULL;
-  char *ret = NULL;
-
-  file = ot_util_new_file_for_path (path);
-  if (!ot_util_gfile_load_contents_utf8 (file, NULL, &ret, NULL, error))
-    goto out;
-
- out:
-  g_clear_object (&file);
-  return ret;
-}
-
-gboolean
-ot_util_gfile_load_contents_utf8 (GFile         *file,
-                                  GCancellable  *cancellable,
-                                  char         **contents_out,
-                                  char         **etag_out,
-                                  GError       **error)
-{
-  char *ret_contents = NULL;
-  char *ret_etag = NULL;
-  gsize len;
-  gboolean ret = FALSE;
-
-  if (!g_file_load_contents (file, cancellable, &ret_contents, &len, &ret_etag, error))
-    goto out;
-  if (!g_utf8_validate (ret_contents, len, NULL))
-    {
-      g_set_error (error,
-                   G_IO_ERROR,
-                   G_IO_ERROR_INVALID_DATA,
-                   "Invalid UTF-8");
-      goto out;
-    }
-
-  if (contents_out)
-    *contents_out = ret_contents;
-  else
-    g_free (ret_contents);
-  ret_contents = NULL;
-  if (etag_out)
-    *etag_out = ret_etag;
-  else
-    g_free (ret_etag);
-  ret_etag = NULL;
-  ret = TRUE;
- out:
-  g_free (ret_contents);
-  g_free (ret_etag);
-  return ret;
-}
-
-GInputStream *
-ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error)
-{
-  GInputStream *ret = NULL;
-  int fd;
-  int flags = O_RDONLY;
-  char *path = NULL;
-
-  path = g_file_get_path (file);
-#ifdef O_NOATIME
-  flags |= O_NOATIME;
-#endif
-  fd = open (path, flags);
-  if (fd < 0)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-
-  ret = (GInputStream*)g_unix_input_stream_new (fd, TRUE);
-  
- out:
-  g_free (path);
-  return ret;
-}
-
-/* Like g_file_new_for_path, but only do local stuff, not GVFS */
-GFile *
-ot_util_new_file_for_path (const char *path)
-{
-  return g_vfs_get_file_for_path (g_vfs_get_local (), path);
-}
diff --git a/src/libotutil/ot-gio-utils.h b/src/libotutil/ot-gio-utils.h
deleted file mode 100644 (file)
index 0acb4dc..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_GIO_UTILS_H__
-#define __OSTREE_GIO_UTILS_H__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-GFile *ot_util_new_file_for_path (const char *path);
-
-gboolean ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error);
-
-char * ot_util_get_file_contents_utf8 (const char *path, GError    **error);
-
-gboolean ot_util_gfile_load_contents_utf8 (GFile         *file,
-                                           GCancellable  *cancellable,
-                                           char         **contents_out,
-                                           char         **etag_out,
-                                           GError       **error);
-
-GInputStream *ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error);
-
-G_END_DECLS
-
-#endif
diff --git a/src/libotutil/ot-opt-utils.c b/src/libotutil/ot-opt-utils.c
deleted file mode 100644 (file)
index 54d107c..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "otutil.h"
-
-void
-ot_util_usage_error (GOptionContext *context, const char *message, GError **error)
-{
-  gchar *help = g_option_context_get_help (context, TRUE, NULL);
-  g_printerr ("%s\n", help);
-  g_free (help);
-  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message);
-}
diff --git a/src/libotutil/ot-opt-utils.h b/src/libotutil/ot-opt-utils.h
deleted file mode 100644 (file)
index b65996f..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_GIO_UTILS_H__
-#define __OSTREE_GIO_UTILS_H__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-void ot_util_usage_error (GOptionContext *context, const char *message, GError **error);
-
-G_END_DECLS
-
-#endif
diff --git a/src/libotutil/ot-unix-utils.c b/src/libotutil/ot-unix-utils.c
deleted file mode 100644 (file)
index e1e8972..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-unix-utils.h"
-
-#include <gio/gio.h>
-#include <gio/gunixoutputstream.h>
-
-#include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-
-gboolean
-ot_util_spawn_pager (GOutputStream  **out_stream,
-                     GError         **error)
-{
-  const char *pager;
-  char *argv[2];
-  int stdin_fd;
-  pid_t pid;
-  gboolean ret = FALSE;
-  GOutputStream *ret_stream = NULL;
-
-  if (!isatty (1))
-    {
-      ret_stream = (GOutputStream*)g_unix_output_stream_new (1, TRUE);
-    }
-  else
-    {
-      pager = g_getenv ("GIT_PAGER");
-      if (pager == NULL)
-        pager = "less";
-      
-      argv[0] = (char*)pager;
-      argv[1] = NULL;
-      
-      if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
-                                     NULL, NULL, &pid, &stdin_fd, NULL, NULL, error))
-        {
-          g_prefix_error (error, "%s", "Failed to spawn pager: ");
-          goto out;
-        }
-      
-      ret_stream = (GOutputStream*)g_unix_output_stream_new (stdin_fd, TRUE);
-    }
-
-  *out_stream = ret_stream;
-  ret_stream = NULL;
-  ret = TRUE;
- out:
-  g_clear_object (&ret_stream);
-  return ret;
-}
-
-static int
-compare_filenames_by_component_length (const char *a,
-                                       const char *b)
-{
-  char *a_slash, *b_slash;
-
-  a_slash = strchr (a, '/');
-  b_slash = strchr (b, '/');
-  while (a_slash && b_slash)
-    {
-      a = a_slash + 1;
-      b = b_slash + 1;
-      a_slash = strchr (a, '/');
-      b_slash = strchr (b, '/');
-    }
-  if (a_slash)
-    return -1;
-  else if (b_slash)
-    return 1;
-  else
-    return 0;
-}
-
-GPtrArray *
-ot_util_sort_filenames_by_component_length (GPtrArray *files)
-{
-  GPtrArray *array = g_ptr_array_sized_new (files->len);
-  memcpy (array->pdata, files->pdata, sizeof (gpointer) * files->len);
-  g_ptr_array_sort (array, (GCompareFunc) compare_filenames_by_component_length);
-  return array;
-}
-
-int
-ot_util_count_filename_components (const char *path)
-{
-  int i = 0;
-
-  while (path)
-    {
-      i++;
-      path = strchr (path, '/');
-      if (path)
-        path++;
-    }
-  return i;
-}
-
-gboolean
-ot_util_filename_has_dotdot (const char *path)
-{
-  char *p;
-  char last;
-
-  if (strcmp (path, "..") == 0)
-    return TRUE;
-  if (g_str_has_prefix (path, "../"))
-    return TRUE;
-  p = strstr (path, "/..");
-  if (!p)
-    return FALSE;
-  last = *(p + 1);
-  return last == '\0' || last == '/';
-}
-
-GPtrArray *
-ot_util_path_split (const char *path)
-{
-  GPtrArray *ret = NULL;
-  const char *p;
-  const char *slash;
-  int i;
-
-  g_return_val_if_fail (path[0] != '/', NULL);
-
-  ret = g_ptr_array_new ();
-  g_ptr_array_set_free_func (ret, g_free);
-
-  p = path;
-  do {
-    slash = strchr (p, '/');
-    if (!slash)
-      {
-        g_ptr_array_add (ret, g_strdup (p));
-        p = NULL;
-      }
-    else
-      {
-        g_ptr_array_add (ret, g_strndup (p, slash - p));
-        p = slash + 1;
-      }
-  } while (p && *p);
-
-  /* Canonicalize by removing duplicate '.' */
-  for (i = ret->len-1; i >= 0; i--)
-    {
-      if (strcmp (ret->pdata[i], ".") == 0)
-        g_ptr_array_remove_index (ret, i);
-    }
-
-  return ret;
-}
-
-char *
-ot_util_path_join_n (const char *base, GPtrArray *components, int n)
-{
-  int max = MIN(n+1, components->len);
-  GPtrArray *subcomponents;
-  char *path;
-  int i;
-
-  subcomponents = g_ptr_array_new ();
-
-  if (base != NULL)
-    g_ptr_array_add (subcomponents, (char*)base);
-
-  for (i = 0; i < max; i++)
-    {
-      g_ptr_array_add (subcomponents, components->pdata[i]);
-    }
-  g_ptr_array_add (subcomponents, NULL);
-  
-  path = g_build_filenamev ((char**)subcomponents->pdata);
-  g_ptr_array_free (subcomponents, TRUE);
-  
-  return path;
-}
-
-void
-ot_util_set_error_from_errno (GError **error,
-                              gint     saved_errno)
-{
-  g_set_error_literal (error,
-                       G_IO_ERROR,
-                       0,
-                       g_strerror (saved_errno));
-  errno = saved_errno;
-}
-
-int
-ot_util_open_file_read (const char *path, GError **error)
-{
-  char *dirname = NULL;
-  char *basename = NULL;
-  DIR *dir = NULL;
-  int fd = -1;
-
-  dirname = g_path_get_dirname (path);
-  basename = g_path_get_basename (path);
-  dir = opendir (dirname);
-  if (dir == NULL)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-
-  fd = ot_util_open_file_read_at (dirfd (dir), basename, error);
-
- out:
-  g_free (basename);
-  g_free (dirname);
-  if (dir != NULL)
-    closedir (dir);
-  return fd;
-}
-
-int
-ot_util_open_file_read_at (int dirfd, const char *name, GError **error)
-{
-  int fd;
-  int flags = O_RDONLY;
-  
-#ifdef O_CLOEXEC
-  flags |= O_CLOEXEC;
-#endif
-#ifdef O_NOATIME
-  flags |= O_NOATIME;
-#endif
-  fd = openat (dirfd, name, flags);
-  if (fd < 0)
-    ot_util_set_error_from_errno (error, errno);
-  return fd;
-}
diff --git a/src/libotutil/ot-unix-utils.h b/src/libotutil/ot-unix-utils.h
deleted file mode 100644 (file)
index 0e1d392..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_UNIX_UTILS_H__
-#define __OSTREE_UNIX_UTILS_H__
-
-#include <gio/gio.h>
-
-/* I just put all this shit here. Sue me. */
-#include <sys/types.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <string.h>
-#include <fcntl.h>
-#include <stdio.h>
-
-G_BEGIN_DECLS
-
-gboolean ot_util_spawn_pager (GOutputStream  **out_stream, GError         **error);
-
-gboolean ot_util_filename_has_dotdot (const char *path);
-
-GPtrArray *ot_util_sort_filenames_by_component_length (GPtrArray *files);
-
-GPtrArray* ot_util_path_split (const char *path);
-
-char *ot_util_path_join_n (const char *base, GPtrArray *components, int n);
-
-int ot_util_count_filename_components (const char *path);
-
-int ot_util_open_file_read (const char *path, GError **error);
-
-int ot_util_open_file_read_at (int dirfd, const char *name, GError **error);
-
-void ot_util_set_error_from_errno (GError **error, gint saved_errno);
-
-G_END_DECLS
-
-#endif
diff --git a/src/libotutil/otutil.h b/src/libotutil/otutil.h
deleted file mode 100644 (file)
index 2c49819..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_UTIL_H__
-
-#include <ot-unix-utils.h>
-#include <ot-gio-utils.h>
-#include <ot-opt-utils.h>
-
-#endif
diff --git a/src/main.c b/src/main.c
deleted file mode 100644 (file)
index ad5dfd8..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "ot-builtins.h"
-
-static OstreeBuiltin builtins[] = {
-  { "checkout", ostree_builtin_checkout, 0 },
-  { "init", ostree_builtin_init, 0 },
-  { "commit", ostree_builtin_commit, 0 },
-  { "link-file", ostree_builtin_link_file, 0 },
-  { "log", ostree_builtin_log, 0 },
-  { "pull", ostree_builtin_pull, 0 },
-  { "fsck", ostree_builtin_fsck, 0 },
-  { "remote", ostree_builtin_remote, 0 },
-  { "rev-parse", ostree_builtin_rev_parse, 0 },
-  { "remote", ostree_builtin_remote, 0 },
-  { "run-triggers", ostree_builtin_run_triggers, 0 },
-  { "show", ostree_builtin_show, 0 },
-  { NULL }
-};
-
-static int
-usage (char **argv, gboolean is_error)
-{
-  OstreeBuiltin *builtin = builtins;
-  void (*print_func) (const gchar *format, ...);
-
-  if (is_error)
-    print_func = g_printerr;
-  else
-    print_func = g_print;
-
-  print_func ("usage: %s COMMAND [options]\n",
-              argv[0]);
-  print_func ("Builtin commands:\n");
-
-  while (builtin->name)
-    {
-      print_func ("  %s\n", builtin->name);
-      builtin++;
-    }
-  return (is_error ? 1 : 0);
-}
-
-
-int
-main (int    argc,
-      char **argv)
-{
-  OstreeBuiltin *builtin;
-  const char *cmd;
-
-  g_type_init ();
-
-  g_set_prgname (argv[0]);
-
-  builtin = builtins;
-
-  if (argc < 2)
-    return usage (argv, 1);
-  
-  cmd = argv[1];
-
-  while (builtin->name)
-    {
-      GError *error = NULL;
-      if (strcmp (cmd, builtin->name) == 0)
-        {
-          int i;
-          int tmp_argc;
-          char **tmp_argv;
-
-          tmp_argc = argc - 1;
-          tmp_argv = g_new0 (char *, tmp_argc + 1);
-
-          tmp_argv[0] = (char*)builtin->name;
-          for (i = 0; i < tmp_argc; i++)
-            tmp_argv[i+1] = argv[i+2];
-          if (!builtin->fn (tmp_argc, tmp_argv, NULL, &error))
-            {
-              g_free (tmp_argv);
-              g_printerr ("%s\n", error->message);
-              g_clear_error (&error);
-              return 1;
-            }
-          g_free (tmp_argv);
-          return 0;
-        }
-      builtin++;
-    }
-  
-  g_printerr ("Unknown command '%s'\n", cmd);
-  return usage (argv, 1);
-}
diff --git a/src/ot-builtin-checkout.c b/src/ot-builtin-checkout.c
deleted file mode 100644 (file)
index 30622fd..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-
-static GOptionEntry options[] = {
-  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
-  { NULL }
-};
-
-gboolean
-ostree_builtin_checkout (int argc, char **argv, const char *prefix, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  OstreeCheckout *checkout = NULL;
-  const char *commit;
-  const char *destination;
-
-  context = g_option_context_new ("COMMIT DESTINATION - Check out a commit into a filesystem tree");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (repo_path == NULL)
-    repo_path = ".";
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc < 3)
-    {
-      gchar *help = g_option_context_get_help (context, TRUE, NULL);
-      g_printerr ("%s\n", help);
-      g_free (help);
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "COMMIT and DESTINATION must be specified");
-      goto out;
-    }
-
-  commit = argv[1];
-  destination = argv[2];
-
-  if (!ostree_repo_checkout (repo, commit, destination, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  g_clear_object (&checkout);
-  return ret;
-}
diff --git a/src/ot-builtin-commit.c b/src/ot-builtin-commit.c
deleted file mode 100644 (file)
index 19af39b..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-static gboolean separator_null;
-static int from_fd = -1;
-static gboolean from_stdin;
-static char *from_file;
-static char *subject;
-static char *body;
-static char *parent;
-static char *branch;
-static char **additions;
-static char **removals;
-
-static GOptionEntry options[] = {
-  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
-  { "subject", 's', 0, G_OPTION_ARG_STRING, &subject, "One line subject", "subject" },
-  { "body", 'm', 0, G_OPTION_ARG_STRING, &body, "Full description", "body" },
-  { "branch", 'b', 0, G_OPTION_ARG_STRING, &branch, "Branch", "branch" },
-  { "parent", 'p', 0, G_OPTION_ARG_STRING, &parent, "Parent commit", "commit" },
-  { "from-fd", 0, 0, G_OPTION_ARG_INT, &from_fd, "Read new tree files from fd", "file descriptor" },
-  { "from-stdin", 0, 0, G_OPTION_ARG_NONE, &from_stdin, "Read new tree files from stdin", "file descriptor" },
-  { "from-file", 0, 0, G_OPTION_ARG_FILENAME, &from_file, "Read new tree files from another file", "path" },
-  { "separator-null", 0, 0, G_OPTION_ARG_NONE, &separator_null, "", "Use '\\0' as filename separator, as with find -print0" },
-  { "add", 'a', 0, G_OPTION_ARG_FILENAME_ARRAY, &additions, "Relative file path to add", "filename" },
-  { "remove", 'r', 0, G_OPTION_ARG_FILENAME_ARRAY, &removals, "Relative file path to remove", "filename" },
-  { NULL }
-};
-
-gboolean
-ostree_builtin_commit (int argc, char **argv, const char *prefix, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  gboolean using_filename_cmdline;
-  gboolean using_filedescriptors;
-  GPtrArray *additions_array = NULL;
-  GPtrArray *removals_array = NULL;
-  GChecksum *commit_checksum = NULL;
-  char **iter;
-
-  context = g_option_context_new ("- Commit a new revision");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (repo_path == NULL)
-    repo_path = ".";
-  if (prefix == NULL)
-    prefix = ".";
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  using_filename_cmdline = (removals || additions);
-  using_filedescriptors = (from_file || from_fd >= 0 || from_stdin);
-
-  if (!(using_filename_cmdline || using_filedescriptors))
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "No additions or removals specified");
-      goto out;
-    }
-  if (using_filename_cmdline && using_filedescriptors)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "File descriptors may not be combined with --add or --remove");
-      goto out;
-    }
-
-  if (!branch)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "A branch must be specified with --branch");
-      goto out;
-    }
-
-  if (!subject)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "A subject must be specified with --subject");
-      goto out;
-    }
-
-  if (using_filename_cmdline)
-    {
-      g_assert (removals || additions);
-      additions_array = g_ptr_array_new ();
-      removals_array = g_ptr_array_new ();
-
-      if (additions)
-        for (iter = additions; *iter; iter++)
-          g_ptr_array_add (additions_array, *iter);
-      if (removals)
-        for (iter = removals; *iter; iter++)
-          g_ptr_array_add (removals_array, *iter);
-      
-      if (!ostree_repo_commit (repo, branch, parent, subject, body, NULL,
-                               prefix, additions_array,
-                               removals_array,
-                               &commit_checksum,
-                               error))
-        goto out;
-    }
-  else if (using_filedescriptors)
-    {
-      char separator = separator_null ? '\0' : '\n';
-      gboolean temp_fd = -1;
-
-      if (from_stdin)
-        from_fd = 0;
-      else if (from_file)
-        {
-          temp_fd = ot_util_open_file_read (from_file, error);
-          if (temp_fd < 0)
-            {
-              g_prefix_error (error, "Failed to open '%s': ", from_file);
-              goto out;
-            }
-          from_fd = temp_fd;
-        }
-      if (!ostree_repo_commit_from_filelist_fd (repo, branch, parent, subject, body, NULL,
-                                                prefix, from_fd, separator,
-                                                &commit_checksum, error))
-        {
-          if (temp_fd >= 0)
-            close (temp_fd);
-          goto out;
-        }
-      if (temp_fd >= 0)
-        close (temp_fd);
-    }
-  ret = TRUE;
-  g_print ("%s\n", g_checksum_get_string (commit_checksum));
- out:
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  if (removals_array)
-    g_ptr_array_free (removals_array, TRUE);
-  if (additions_array)
-    g_ptr_array_free (additions_array, TRUE);
-  if (commit_checksum)
-    g_checksum_free (commit_checksum);
-  return ret;
-}
diff --git a/src/ot-builtin-fsck.c b/src/ot-builtin-fsck.c
deleted file mode 100644 (file)
index 3a21752..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-static gboolean quiet;
-
-static GOptionEntry options[] = {
-  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", NULL },
-  { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
-  { NULL }
-};
-
-typedef struct {
-  guint n_objects;
-} HtFsckData;
-
-static gboolean
-checksum_packed_file (HtFsckData   *data,
-                      const char   *path,
-                      GChecksum   **out_checksum,
-                      GError      **error)
-{
-  gboolean ret = FALSE;
-  GChecksum *ret_checksum = NULL;
-  GFile *file = NULL;
-  char *metadata_buf = NULL;
-  GVariant *metadata = NULL;
-  GVariant *xattrs = NULL;
-  GFileInputStream *in = NULL;
-  guint32 metadata_len;
-  guint32 version, uid, gid, mode;
-  guint64 content_len;
-  gsize bytes_read;
-  char buf[8192];
-
-  file = ot_util_new_file_for_path (path);
-
-  in = g_file_read (file, NULL, error);
-  if (!in)
-    goto out;
-      
-  if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
-    goto out;
-      
-  metadata_len = GUINT32_FROM_BE (metadata_len);
-      
-  metadata_buf = g_malloc (metadata_len);
-      
-  if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
-    goto out;
-
-  metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
-                                      metadata_buf, metadata_len, FALSE, NULL, NULL);
-      
-  g_variant_get (metadata, "(uuuu@a(ayay)t)",
-                 &version, &uid, &gid, &mode,
-                 &xattrs, &content_len);
-  uid = GUINT32_FROM_BE (uid);
-  gid = GUINT32_FROM_BE (gid);
-  mode = GUINT32_FROM_BE (mode);
-  content_len = GUINT64_FROM_BE (content_len);
-
-  ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
-
-  do
-    {
-      if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
-        goto out;
-      g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
-    }
-  while (bytes_read > 0);
-
-  ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
-  g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
-
-  ret = TRUE;
-  *out_checksum = ret_checksum;
-  ret_checksum = NULL;
- out:
-  if (ret_checksum)
-    g_checksum_free (ret_checksum);
-  g_free (metadata_buf);
-  g_clear_object (&file);
-  g_clear_object (&in);
-  if (metadata)
-   g_variant_unref (metadata);
-  if (xattrs)
-    g_variant_unref (xattrs);
-  return ret;
-}
-                    
-
-static void
-object_iter_callback (OstreeRepo  *repo,
-                      const char    *path,
-                      GFileInfo     *file_info,
-                      gpointer       user_data)
-{
-  HtFsckData *data = user_data;
-  struct stat stbuf;
-  GChecksum *checksum = NULL;
-  GError *error = NULL;
-  char *dirname = NULL;
-  char *checksum_prefix = NULL;
-  char *checksum_string = NULL;
-  char *filename_checksum = NULL;
-  gboolean packed = FALSE;
-  OstreeObjectType objtype;
-  char *dot;
-
-  /* nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
-     if (nlinks < 2 && !quiet)
-     g_printerr ("note: floating object: %s\n", path); */
-
-  if (g_str_has_suffix (path, ".meta"))
-    objtype = OSTREE_OBJECT_TYPE_META;
-  else if (g_str_has_suffix (path, ".file"))
-    objtype = OSTREE_OBJECT_TYPE_FILE;
-  else if (g_str_has_suffix (path, ".packfile"))
-    {
-      objtype = OSTREE_OBJECT_TYPE_FILE;
-     packed = TRUE;
-    }
-  else
-    g_assert_not_reached ();
-
-  if (packed && objtype == OSTREE_OBJECT_TYPE_FILE)
-    {
-      if (!checksum_packed_file (data, path, &checksum, &error))
-        goto out;
-    }
-  else
-    {
-      if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, &error))
-        goto out;
-    }
-
-  filename_checksum = g_strdup (g_file_info_get_name (file_info));
-  dot = strrchr (filename_checksum, '.');
-  g_assert (dot != NULL);
-  *dot = '\0';
-  
-  dirname = g_path_get_dirname (path);
-  checksum_prefix = g_path_get_basename (dirname);
-  checksum_string = g_strconcat (checksum_prefix, filename_checksum, NULL);
-  
-  if (strcmp (checksum_string, g_checksum_get_string (checksum)) != 0)
-    {
-      g_printerr ("ERROR: corrupted object '%s' expected checksum: %s\n",
-                  path, g_checksum_get_string (checksum));
-    }
-
-  data->n_objects++;
-
- out:
-  if (checksum != NULL)
-    g_checksum_free (checksum);
-  g_free (dirname);
-  g_free (checksum_prefix);
-  g_free (checksum_string);
-  g_free (filename_checksum);
-  if (error != NULL)
-    {
-      g_printerr ("%s\n", error->message);
-      g_clear_error (&error);
-    }
-}
-
-gboolean
-ostree_builtin_fsck (int argc, char **argv, const char *prefix, GError **error)
-{
-  GOptionContext *context;
-  HtFsckData data;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-
-  context = g_option_context_new ("- Check the repository for consistency");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (repo_path == NULL)
-    repo_path = ".";
-
-  data.n_objects = 0;
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (!ostree_repo_iter_objects (repo, object_iter_callback, &data, error))
-    goto out;
-
-  if (!quiet)
-    g_printerr ("Total Objects: %u\n", data.n_objects);
-
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  return ret;
-}
diff --git a/src/ot-builtin-init.c b/src/ot-builtin-init.c
deleted file mode 100644 (file)
index d16b57c..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-static gboolean archive;
-
-static GOptionEntry options[] = {
-  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", NULL },
-  { "archive", 0, 0, G_OPTION_ARG_NONE, &archive, "Initialize repository as archive", NULL },
-  { NULL }
-};
-
-#define DEFAULT_CONFIG_CONTENTS ("[core]\n" \
-                                 "repo_version=0\n")
-
-
-gboolean
-ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error)
-{
-  GOptionContext *context = NULL;
-  gboolean ret = FALSE;
-  GFile *repodir = NULL;
-  GFile *child = NULL;
-  GFile *grandchild = NULL;
-  GString *config_data = NULL;
-
-  context = g_option_context_new ("- Initialize a new empty repository");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (repo_path == NULL)
-    repo_path = ".";
-
-  repodir = ot_util_new_file_for_path (repo_path);
-
-  child = g_file_get_child (repodir, "config");
-
-  config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
-  g_string_append_printf (config_data, "archive=%s\n", archive ? "true" : "false");
-  if (!g_file_replace_contents (child,
-                                config_data->str,
-                                config_data->len,
-                                NULL, FALSE, 0, NULL,
-                                NULL, error))
-    goto out;
-  g_clear_object (&child);
-
-  child = g_file_get_child (repodir, "objects");
-  if (!g_file_make_directory (child, NULL, error))
-    goto out;
-  g_clear_object (&child);
-
-  child = g_file_get_child (repodir, "refs");
-  if (!g_file_make_directory (child, NULL, error))
-    goto out;
-
-  grandchild = g_file_get_child (child, "heads");
-  if (!g_file_make_directory (grandchild, NULL, error))
-    goto out;
-  g_clear_object (&grandchild);
-
-  grandchild = g_file_get_child (child, "remotes");
-  if (!g_file_make_directory (grandchild, NULL, error))
-    goto out;
-  g_clear_object (&grandchild);
-
-  g_clear_object (&child);
-
-  child = g_file_get_child (repodir, "tags");
-  if (!g_file_make_directory (child, NULL, error))
-    goto out;
-  g_clear_object (&child);
-
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  if (config_data)
-    g_string_free (config_data, TRUE);
-  g_clear_object (&repodir);
-  g_clear_object (&child);
-  g_clear_object (&grandchild);
-  return ret;
-}
diff --git a/src/ot-builtin-link-file.c b/src/ot-builtin-link-file.c
deleted file mode 100644 (file)
index dddec44..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-static gboolean ignore_exists;
-static gboolean force;
-
-static GOptionEntry options[] = {
-  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
-  { "ignore-exists", 'n', 0, G_OPTION_ARG_NONE, &ignore_exists, "Don't error if file exists", NULL },
-  { "force", 'f', 0, G_OPTION_ARG_NONE, &force, "If object exists, relink file", NULL },
-  { NULL }
-};
-
-gboolean
-ostree_builtin_link_file (int argc, char **argv, const char *prefix, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  int i;
-
-  context = g_option_context_new ("- Create a new hard link in the repository");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (repo_path == NULL)
-    repo_path = ".";
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  for (i = 0; i < argc-1; i++)
-    {
-      if (!ostree_repo_link_file (repo, argv[i+1], ignore_exists, force, error))
-        goto out;
-    }
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  return ret;
-}
diff --git a/src/ot-builtin-log.c b/src/ot-builtin-log.c
deleted file mode 100644 (file)
index c6d725b..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-
-static GOptionEntry options[] = {
-  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
-  { NULL }
-};
-
-gboolean
-ostree_builtin_log (int argc, char **argv, const char *prefix, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  GOutputStream *pager = NULL;
-  const char *rev;
-  GVariant *commit = NULL;
-  char *resolved_rev = NULL;
-
-  context = g_option_context_new ("- Show revision log");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (repo_path == NULL)
-    repo_path = ".";
-  if (prefix == NULL)
-    prefix = ".";
-
-  if (argc < 2)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "A revision must be specified");
-      goto out;
-    }
-                   
-  rev = argv[1];
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (!ot_util_spawn_pager (&pager, error))
-    goto out;
-
-  if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
-    goto out;
-
-  while (TRUE)
-    {
-      OstreeSerializedVariantType type;
-      char *formatted = NULL;
-      guint32 version;
-      const char *parent;
-      const char *subject;
-      const char *body;
-      guint64 timestamp;
-      const char *contents;
-      const char *root_metadata;
-      GDateTime *time_obj = NULL;
-      char *formatted_date = NULL;
-      const char *body_newline;
-      gsize bytes_written;
-      GVariant *commit_metadata = NULL;
-      char *formatted_metadata = NULL;
-      
-      if (commit)
-        g_variant_unref (commit);
-      if (!ostree_repo_load_variant (repo, resolved_rev, &type, &commit, error))
-        goto out;
-
-      /* Ignore commit metadata for now */
-      g_variant_get (commit, "(u@a{sv}&s&s&st&s&s)",
-                     &version, &commit_metadata, &parent, &subject, &body,
-                     &timestamp, &contents, &root_metadata);
-      version = GUINT32_FROM_BE (version);
-      timestamp = GUINT64_FROM_BE (timestamp);
-      time_obj = g_date_time_new_from_unix_utc (timestamp);
-      formatted_date = g_date_time_format (time_obj, "%a %b %d %H:%M:%S %Y %z");
-      g_date_time_unref (time_obj);
-      time_obj = NULL;
-
-      formatted_metadata = g_variant_print (commit_metadata, TRUE);
-      g_variant_unref (commit_metadata);
-      formatted = g_strdup_printf ("commit %s\nSubject: %s\nDate: %s\nMetadata: %s\n\n",
-                                   resolved_rev, subject, formatted_date, formatted_metadata);
-      g_free (formatted_metadata);
-      g_free (formatted_date);
-      formatted_date = NULL;
-      
-      if (!g_output_stream_write_all (pager, formatted, strlen (formatted), &bytes_written, NULL, error))
-        {
-          g_free (formatted);
-          goto out;
-        }
-      g_free (formatted);
-      
-      body_newline = strchr (body, '\n');
-      do {
-        gsize len;
-        if (!g_output_stream_write_all (pager, "    ", 4, &bytes_written, NULL, error))
-          goto out;
-        len = body_newline ? body_newline - body : strlen (body);
-        if (!g_output_stream_write_all (pager, body, len, &bytes_written, NULL, error))
-          goto out;
-        if (!g_output_stream_write_all (pager, "\n\n", 2, &bytes_written, NULL, error))
-          goto out;
-        body_newline = strchr (body, '\n');
-        if (!body_newline)
-          break;
-        else
-          body_newline += 1;
-      } while (*body_newline);
-
-      if (strcmp (parent, "") == 0)
-        break;
-      g_free (resolved_rev);
-      resolved_rev = g_strdup (parent);
-    }
-
-  if (!g_output_stream_close (pager, NULL, error))
-    goto out;
-  ret = TRUE;
- out:
-  g_free (resolved_rev);
-  if (context)
-    g_option_context_free (context);
-  if (commit)
-    g_variant_unref (commit);
-  g_clear_object (&repo);
-  return ret;
-}
diff --git a/src/ot-builtin-pull.c b/src/ot-builtin-pull.c
deleted file mode 100644 (file)
index 65a5206..0000000
+++ /dev/null
@@ -1,355 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-#include <libsoup/soup-gnome.h>
-
-static char *repo_path;
-
-static GOptionEntry options[] = {
-  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
-  { NULL }
-};
-
-static gboolean
-fetch_uri (OstreeRepo  *repo,
-           SoupSession *soup,
-           SoupURI     *uri,
-           char       **temp_filename,
-           GError     **error)
-{
-  gboolean ret = FALSE;
-  SoupMessage *msg = NULL;
-  guint response;
-  char *template = NULL;
-  int fd;
-  SoupBuffer *buf = NULL;
-  GFile *tempf = NULL;
-  
-  msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
-  
-  response = soup_session_send_message (soup, msg);
-  if (response != 200)
-    {
-      char *uri_string = soup_uri_to_string (uri, FALSE);
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Failed to retrieve '%s': %d %s",
-                   uri_string, response, msg->reason_phrase);
-      g_free (uri_string);
-      goto out;
-    }
-
-  template = g_strdup_printf ("%s/tmp-fetchXXXXXX", ostree_repo_get_path (repo));
-  
-  fd = g_mkstemp (template);
-  if (fd < 0)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-  close (fd);
-  tempf = ot_util_new_file_for_path (template);
-
-  buf = soup_message_body_flatten (msg->response_body);
-
-  if (!g_file_replace_contents (tempf, buf->data, buf->length, NULL, FALSE, 0, NULL, NULL, error))
-    goto out;
-  
-  *temp_filename = template;
-  template = NULL;
-
-  ret = TRUE;
- out:
-  g_free (template);
-  g_clear_object (&msg);
-  g_clear_object (&tempf);
-  return ret;
-}
-
-static gboolean
-store_object (OstreeRepo  *repo,
-              SoupSession *soup,
-              SoupURI     *baseuri,
-              const char  *object,
-              OstreeObjectType objtype,
-              gboolean    *did_exist,
-              GError     **error)
-{
-  gboolean ret = FALSE;
-  char *filename = NULL;
-  char *objpath = NULL;
-  char *relpath = NULL;
-  SoupURI *obj_uri = NULL;
-
-  objpath = ostree_get_relative_object_path (object, objtype, TRUE);
-  obj_uri = soup_uri_copy (baseuri);
-  relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL);
-  soup_uri_set_path (obj_uri, relpath);
-
-  if (!fetch_uri (repo, soup, obj_uri, &filename, error))
-    goto out;
-
-  if (!ostree_repo_store_packfile (repo, object, filename, objtype, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  if (obj_uri)
-    soup_uri_free (obj_uri);
-  if (filename)
-    (void) unlink (filename);
-  g_free (filename);
-  g_free (objpath);
-  g_free (relpath);
-  return ret;
-}
-
-static gboolean
-store_tree_recurse (OstreeRepo   *repo,
-                    SoupSession  *soup,
-                    SoupURI      *base_uri,
-                    const char   *rev,
-                    GError      **error)
-{
-  gboolean ret = FALSE;
-  GVariant *tree = NULL;
-  GVariant *files_variant = NULL;
-  GVariant *dirs_variant = NULL;
-  OstreeSerializedVariantType metatype;
-  gboolean did_exist;
-  int i, n;
-
-  if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
-    goto out;
-
-  if (!did_exist)
-    {
-      if (!ostree_repo_load_variant (repo, rev, &metatype, &tree, error))
-        goto out;
-      
-      if (metatype != OSTREE_SERIALIZED_TREE_VARIANT)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Tree metadata '%s' has wrong type %d, expected %d",
-                       rev, metatype, OSTREE_SERIALIZED_TREE_VARIANT);
-          goto out;
-        }
-      
-      /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
-      g_variant_get_child (tree, 2, "@a(ss)", &files_variant);
-      g_variant_get_child (tree, 3, "@a(sss)", &dirs_variant);
-      
-      n = g_variant_n_children (files_variant);
-      for (i = 0; i < n; i++)
-        {
-          const char *filename;
-          const char *checksum;
-
-          g_variant_get_child (files_variant, i, "(ss)", &filename, &checksum);
-
-          if (!store_object (repo, soup, base_uri, checksum, OSTREE_OBJECT_TYPE_FILE, &did_exist, error))
-            goto out;
-        }
-      
-      for (i = 0; i < n; i++)
-        {
-          const char *dirname;
-          const char *tree_checksum;
-          const char *meta_checksum;
-
-          g_variant_get_child (dirs_variant, i, "(sss)",
-                               &dirname, &tree_checksum, &meta_checksum);
-
-          if (!store_tree_recurse (repo, soup, base_uri, tree_checksum, error))
-            goto out;
-
-          if (!store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
-            goto out;
-        }
-    }
-
-  ret = TRUE;
- out:
-  if (tree)
-    g_variant_unref (tree);
-  if (files_variant)
-    g_variant_unref (files_variant);
-  if (dirs_variant)
-    g_variant_unref (dirs_variant);
-  return ret;
-}
-
-static gboolean
-store_commit_recurse (OstreeRepo   *repo,
-                      SoupSession  *soup,
-                      SoupURI      *base_uri,
-                      const char   *rev,
-                      GError      **error)
-{
-  gboolean ret = FALSE;
-  GVariant *commit = NULL;
-  OstreeSerializedVariantType metatype;
-  const char *tree_contents_checksum;
-  const char *tree_meta_checksum;
-  gboolean did_exist;
-
-  if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
-    goto out;
-
-  if (!did_exist)
-    {
-      if (!ostree_repo_load_variant (repo, rev, &metatype, &commit, error))
-        goto out;
-      
-      if (metatype != OSTREE_SERIALIZED_COMMIT_VARIANT)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Commit '%s' has wrong type %d, expected %d",
-                       rev, metatype, OSTREE_SERIALIZED_COMMIT_VARIANT);
-          goto out;
-        }
-      
-      /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
-      g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
-      g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
-      
-      if (!store_object (repo, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
-        goto out;
-      
-      if (!store_tree_recurse (repo, soup, base_uri, tree_contents_checksum, error))
-        goto out;
-    }
-
-  ret = TRUE;
- out:
-  if (commit)
-    g_variant_unref (commit);
-  return ret;
-}
-                      
-gboolean
-ostree_builtin_pull (int argc, char **argv, const char *prefix, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  const char *remote;
-  const char *branch;
-  char *remote_branch_ref_path = NULL;
-  char *key = NULL;
-  char *baseurl = NULL;
-  char *refpath = NULL;
-  char *temppath = NULL;
-  GKeyFile *config = NULL;
-  SoupURI *base_uri = NULL;
-  SoupURI *target_uri = NULL;
-  SoupSession *soup = NULL;
-  char *rev = NULL;
-
-  context = g_option_context_new ("REMOTE BRANCH - Download data from remote repository");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (repo_path == NULL)
-    repo_path = ".";
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc < 3)
-    {
-      ot_util_usage_error (context, "REMOTE and BRANCH must be specified", error);
-      goto out;
-    }
-
-  remote = argv[1];
-  branch = argv[2];
-
-  config = ostree_repo_get_config (repo);
-
-  key = g_strdup_printf ("remote \"%s\"", remote);
-  baseurl = g_key_file_get_string (config, key, "url", error);
-  if (!baseurl)
-    goto out;
-  base_uri = soup_uri_new (baseurl);
-  if (!base_uri)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Failed to parse url '%s'", baseurl);
-      goto out;
-    }
-  target_uri = soup_uri_copy (base_uri);
-  g_free (refpath);
-  refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", branch, NULL);
-  soup_uri_set_path (target_uri, refpath);
-  
-  soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
-                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
-                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
-                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
-                                             NULL);
-  if (!fetch_uri (repo, soup, target_uri, &temppath, error))
-    goto out;
-
-  rev = ot_util_get_file_contents_utf8 (temppath, error);
-  if (!rev)
-    goto out;
-  g_strchomp (rev);
-
-  if (!ostree_validate_checksum_string (rev, error))
-    goto out;
-
-  if (!store_commit_recurse (repo, soup, base_uri, rev, error))
-    goto out;
-
-  if (!ostree_repo_write_ref (repo, FALSE, branch, rev, error))
-    goto out;
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  if (temppath)
-    (void) unlink (temppath);
-  g_free (temppath);
-  g_free (key);
-  g_free (rev);
-  g_free (baseurl);
-  g_free (refpath);
-  g_free (remote_branch_ref_path);
-  g_clear_object (&soup);
-  if (base_uri)
-    soup_uri_free (base_uri);
-  if (target_uri)
-    soup_uri_free (target_uri);
-  g_clear_object (&repo);
-  g_clear_object (&soup);
-  return ret;
-}
diff --git a/src/ot-builtin-remote.c b/src/ot-builtin-remote.c
deleted file mode 100644 (file)
index 40f7c29..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-
-static GOptionEntry options[] = {
-  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
-  { NULL }
-};
-
-static void
-usage_error (GOptionContext *context, const char *message, GError **error)
-{
-  gchar *help = g_option_context_get_help (context, TRUE, NULL);
-  g_printerr ("%s\n", help);
-  g_free (help);
-  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       message);
-}
-
-gboolean
-ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  OstreeCheckout *checkout = NULL;
-  const char *op;
-  GKeyFile *config = NULL;
-
-  context = g_option_context_new ("OPERATION [args] - Control remote repository configuration");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (repo_path == NULL)
-    repo_path = ".";
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc < 2)
-    {
-      usage_error (context, "OPERATION must be specified", error);
-      goto out;
-    }
-
-  op = argv[1];
-
-  config = ostree_repo_copy_config (repo);
-
-  if (!strcmp (op, "add"))
-    {
-      char *key;
-      if (argc < 4)
-        {
-          usage_error (context, "NAME and URL must be specified", error);
-          goto out;
-        }
-      key = g_strdup_printf ("remote \"%s\"", argv[2]);
-      g_key_file_set_string (config, key, "url", argv[3]);
-      g_free (key);
-    }
-  else
-    {
-      usage_error (context, "Unknown operation", error);
-      goto out;
-    }
-
-  if (!ostree_repo_write_config (repo, config, error))
-    goto out;
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  if (config)
-    g_key_file_unref (config);
-  g_clear_object (&repo);
-  g_clear_object (&checkout);
-  return ret;
-}
diff --git a/src/ot-builtin-rev-parse.c b/src/ot-builtin-rev-parse.c
deleted file mode 100644 (file)
index 6e86d6c..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-
-static GOptionEntry options[] = {
-  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
-  { NULL }
-};
-
-gboolean
-ostree_builtin_rev_parse (int argc, char **argv, const char *prefix, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  const char *rev = "master";
-  char *resolved_rev = NULL;
-  GVariant *variant = NULL;
-  char *formatted_variant = NULL;
-
-  context = g_option_context_new ("REV - Output the target of a rev");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (repo_path == NULL)
-    repo_path = ".";
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc < 2)
-    {
-      ot_util_usage_error (context, "REV must be specified", error);
-      goto out;
-    }
-  rev = argv[1];
-
-  if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
-    goto out;
-
-  g_print ("%s\n", resolved_rev);
-  ret = TRUE;
- out:
-  g_free (resolved_rev);
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  if (variant)
-    g_variant_unref (variant);
-  g_free (formatted_variant);
-  return ret;
-}
diff --git a/src/ot-builtin-run-triggers.c b/src/ot-builtin-run-triggers.c
deleted file mode 100644 (file)
index 4959156..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-static gboolean quiet;
-
-static GOptionEntry options[] = {
-  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
-  { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
-  { NULL }
-};
-
-gboolean
-ostree_builtin_run_triggers (int argc, char **argv, const char *prefix, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  OstreeCheckout *checkout = NULL;
-  const char *dir;
-
-  context = g_option_context_new ("DIR - Run trigger scripts for directory");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (repo_path == NULL)
-    repo_path = ".";
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc < 1)
-    {
-      gchar *help = g_option_context_get_help (context, TRUE, NULL);
-      g_printerr ("%s\n", help);
-      g_free (help);
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "DIR must be specified");
-      goto out;
-    }
-
-  dir = argv[1];
-
-  checkout = ostree_checkout_new (repo, dir);
-  if (!ostree_checkout_run_triggers (checkout, error))
-    goto out;
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  g_clear_object (&checkout);
-  return ret;
-}
diff --git a/src/ot-builtin-show.c b/src/ot-builtin-show.c
deleted file mode 100644 (file)
index 8985c56..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-
-static GOptionEntry options[] = {
-  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
-  { NULL }
-};
-
-gboolean
-ostree_builtin_show (int argc, char **argv, const char *prefix, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  const char *rev = "master";
-  char *resolved_rev = NULL;
-  OstreeSerializedVariantType type;
-  GVariant *variant = NULL;
-  char *formatted_variant = NULL;
-
-  context = g_option_context_new ("- Output a metadata object");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (repo_path == NULL)
-    repo_path = ".";
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc > 1)
-    rev = argv[1];
-
-  if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
-    goto out;
-
-  if (!ostree_repo_load_variant (repo, resolved_rev, &type, &variant, error))
-    goto out;
-
-  g_print ("Object: %s\nType: %d\n", resolved_rev, type);
-  formatted_variant = g_variant_print (variant, TRUE);
-  g_print ("%s\n", formatted_variant);
-  ret = TRUE;
- out:
-  g_free (resolved_rev);
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  if (variant)
-    g_variant_unref (variant);
-  g_free (formatted_variant);
-  return ret;
-}
diff --git a/src/ot-builtins.h b/src/ot-builtins.h
deleted file mode 100644 (file)
index 1373f60..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_BUILTINS__
-#define __OSTREE_BUILTINS__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-typedef enum {
-  OSTREE_BUILTIN_FLAG_NONE = 0,
-} OstreeBuiltinFlags;
-
-typedef struct {
-  const char *name;
-  gboolean (*fn) (int argc, char **argv, const char *prefix, GError **error);
-  int flags; /* OstreeBuiltinFlags */
-} OstreeBuiltin;
-
-gboolean ostree_builtin_checkout (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_commit (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_log (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_link_file (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_pull (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_run_triggers (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_fsck (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_show (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_rev_parse (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error);
-
-G_END_DECLS
-
-#endif